summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/.gitignore67
-rw-r--r--tests/Makefile10
-rw-r--r--tests/bgpd/subdir.am82
-rw-r--r--tests/bgpd/test_aspath.c1463
-rw-r--r--tests/bgpd/test_aspath.py84
-rw-r--r--tests/bgpd/test_bgp_table.c167
-rw-r--r--tests/bgpd/test_bgp_table.py9
-rw-r--r--tests/bgpd/test_capability.c984
-rw-r--r--tests/bgpd/test_capability.py56
-rw-r--r--tests/bgpd/test_ecommunity.c134
-rw-r--r--tests/bgpd/test_ecommunity.py11
-rw-r--r--tests/bgpd/test_mp_attr.c1117
-rw-r--r--tests/bgpd/test_mp_attr.py49
-rw-r--r--tests/bgpd/test_mpath.c482
-rw-r--r--tests/bgpd/test_mpath.py10
-rw-r--r--tests/bgpd/test_packet.c75
-rw-r--r--tests/bgpd/test_peer_attr.c1491
-rw-r--r--tests/bgpd/test_peer_attr.py200
-rw-r--r--tests/helpers/c/main.c172
-rw-r--r--tests/helpers/c/prng.c95
-rw-r--r--tests/helpers/c/prng.h22
-rw-r--r--tests/helpers/c/tests.h17
-rw-r--r--tests/helpers/python/frrsix.py72
-rw-r--r--tests/helpers/python/frrtest.py211
-rw-r--r--tests/isisd/.gitignore1
-rw-r--r--tests/isisd/subdir.am66
-rw-r--r--tests/isisd/test_common.c339
-rw-r--r--tests/isisd/test_common.h72
-rw-r--r--tests/isisd/test_fuzz_isis_tlv.c191
-rw-r--r--tests/isisd/test_fuzz_isis_tlv.py32
-rw-r--r--tests/isisd/test_fuzz_isis_tlv_tests.h.gzbin0 -> 221814 bytes
-rw-r--r--tests/isisd/test_isis_lspdb.c81
-rw-r--r--tests/isisd/test_isis_lspdb.py8
-rw-r--r--tests/isisd/test_isis_spf.c561
-rw-r--r--tests/isisd/test_isis_spf.in68
-rw-r--r--tests/isisd/test_isis_spf.py5
-rw-r--r--tests/isisd/test_isis_spf.refout4800
-rw-r--r--tests/isisd/test_isis_vertex_queue.c106
-rw-r--r--tests/isisd/test_isis_vertex_queue.py8
-rw-r--r--tests/isisd/test_topologies.c3474
-rw-r--r--tests/lib/cli/.gitignore1
-rw-r--r--tests/lib/cli/common_cli.c89
-rw-r--r--tests/lib/cli/common_cli.h42
-rw-r--r--tests/lib/cli/test_cli.c76
-rw-r--r--tests/lib/cli/test_cli.in105
-rw-r--r--tests/lib/cli/test_cli.py6
-rw-r--r--tests/lib/cli/test_cli.refout.in431
-rw-r--r--tests/lib/cli/test_commands.c401
-rw-r--r--tests/lib/cli/test_commands.in216
-rw-r--r--tests/lib/cli/test_commands.py13
-rw-r--r--tests/lib/cli/test_commands.refout1007
-rw-r--r--tests/lib/cxxcompat.c109
-rw-r--r--tests/lib/fuzz_zlog.c117
-rw-r--r--tests/lib/fuzz_zlog_inputs.py28
-rw-r--r--tests/lib/northbound/test_oper_data.c403
-rw-r--r--tests/lib/northbound/test_oper_data.in1
-rw-r--r--tests/lib/northbound/test_oper_data.py5
-rw-r--r--tests/lib/northbound/test_oper_data.refout119
-rw-r--r--tests/lib/script1.lua54
-rw-r--r--tests/lib/subdir.am385
-rw-r--r--tests/lib/test_assert.c51
-rw-r--r--tests/lib/test_assert.py56
-rw-r--r--tests/lib/test_atomlist.c394
-rw-r--r--tests/lib/test_atomlist.py8
-rw-r--r--tests/lib/test_buffer.c42
-rw-r--r--tests/lib/test_checksum.c572
-rw-r--r--tests/lib/test_darr.c279
-rw-r--r--tests/lib/test_frrlua.c99
-rw-r--r--tests/lib/test_frrlua.py14
-rw-r--r--tests/lib/test_frrscript.c99
-rw-r--r--tests/lib/test_frrscript.py14
-rw-r--r--tests/lib/test_graph.c64
-rw-r--r--tests/lib/test_graph.py5
-rw-r--r--tests/lib/test_graph.refout64
-rw-r--r--tests/lib/test_grpc.cpp977
-rw-r--r--tests/lib/test_grpc.py33
-rw-r--r--tests/lib/test_heavy.c89
-rw-r--r--tests/lib/test_heavy_thread.c113
-rw-r--r--tests/lib/test_heavy_wq.c138
-rw-r--r--tests/lib/test_idalloc.c197
-rw-r--r--tests/lib/test_idalloc.py8
-rw-r--r--tests/lib/test_memory.c106
-rw-r--r--tests/lib/test_nexthop.c186
-rw-r--r--tests/lib/test_nexthop.py8
-rw-r--r--tests/lib/test_nexthop_iter.c300
-rw-r--r--tests/lib/test_nexthop_iter.py9
-rw-r--r--tests/lib/test_ntop.c73
-rw-r--r--tests/lib/test_ntop.py8
-rw-r--r--tests/lib/test_plist.c35
-rw-r--r--tests/lib/test_prefix2str.c67
-rw-r--r--tests/lib/test_prefix2str.py8
-rw-r--r--tests/lib/test_printfrr.c409
-rw-r--r--tests/lib/test_printfrr.py8
-rw-r--r--tests/lib/test_privs.c123
-rw-r--r--tests/lib/test_resolver.c68
-rw-r--r--tests/lib/test_ringbuf.c177
-rw-r--r--tests/lib/test_ringbuf.py8
-rw-r--r--tests/lib/test_segv.c67
-rw-r--r--tests/lib/test_seqlock.c114
-rw-r--r--tests/lib/test_sig.c52
-rw-r--r--tests/lib/test_skiplist.c122
-rw-r--r--tests/lib/test_srcdest_table.c422
-rw-r--r--tests/lib/test_srcdest_table.py8
-rw-r--r--tests/lib/test_stream.c57
-rw-r--r--tests/lib/test_stream.py5
-rw-r--r--tests/lib/test_stream.refout8
-rw-r--r--tests/lib/test_table.c496
-rw-r--r--tests/lib/test_table.py12
-rw-r--r--tests/lib/test_timer_correctness.c173
-rw-r--r--tests/lib/test_timer_correctness.py8
-rw-r--r--tests/lib/test_timer_performance.c86
-rw-r--r--tests/lib/test_ttable.c169
-rw-r--r--tests/lib/test_ttable.py5
-rw-r--r--tests/lib/test_ttable.refout143
-rw-r--r--tests/lib/test_typelist.c161
-rw-r--r--tests/lib/test_typelist.h822
-rw-r--r--tests/lib/test_typelist.py21
-rw-r--r--tests/lib/test_versioncmp.c53
-rw-r--r--tests/lib/test_versioncmp.py8
-rw-r--r--tests/lib/test_xref.c127
-rw-r--r--tests/lib/test_xref.py6
-rw-r--r--tests/lib/test_zlog.c49
-rw-r--r--tests/lib/test_zlog.py5
-rw-r--r--tests/lib/test_zmq.c312
-rw-r--r--tests/lib/test_zmq.py14
-rw-r--r--tests/lib/test_zmq.refout67
-rw-r--r--tests/ospf6d/subdir.am19
-rw-r--r--tests/ospf6d/test_lsdb.c246
-rw-r--r--tests/ospf6d/test_lsdb.in72
-rw-r--r--tests/ospf6d/test_lsdb.py5
-rw-r--r--tests/ospf6d/test_lsdb.refout192
-rw-r--r--tests/ospfd/.gitignore3
-rw-r--r--tests/ospfd/common.c249
-rw-r--r--tests/ospfd/common.h47
-rw-r--r--tests/ospfd/subdir.am21
-rw-r--r--tests/ospfd/test_ospf_spf.c305
-rw-r--r--tests/ospfd/test_ospf_spf.in10
-rw-r--r--tests/ospfd/test_ospf_spf.py4
-rw-r--r--tests/ospfd/test_ospf_spf.refout130
-rw-r--r--tests/ospfd/topologies.c575
-rw-r--r--tests/pytest.ini2
-rw-r--r--tests/runtests.py6
-rw-r--r--tests/subdir.am76
-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.ref74
-rw-r--r--tests/topotests/all_protocol_startup/r1/ipv4_routes.ref32
-rw-r--r--tests/topotests/all_protocol_startup/r1/ipv6_nht.ref15
-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.conf20
-rw-r--r--tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v416
-rw-r--r--tests/topotests/all_protocol_startup/r1/ospfd.conf25
-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.ref26
-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.py1724
-rwxr-xr-xtests/topotests/analyze.py433
-rw-r--r--tests/topotests/babel_topo1/r1/babeld.conf20
-rw-r--r--tests/topotests/babel_topo1/r1/show_ip_route.json_ref80
-rw-r--r--tests/topotests/babel_topo1/r1/zebra.conf22
-rw-r--r--tests/topotests/babel_topo1/r2/babeld.conf17
-rw-r--r--tests/topotests/babel_topo1/r2/show_ip_route.json_ref80
-rw-r--r--tests/topotests/babel_topo1/r2/zebra.conf23
-rw-r--r--tests/topotests/babel_topo1/r3/babeld.conf16
-rw-r--r--tests/topotests/babel_topo1/r3/show_ip_route.json_ref81
-rw-r--r--tests/topotests/babel_topo1/r3/zebra.conf25
-rw-r--r--tests/topotests/babel_topo1/test_babel_topo1.py168
-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.py234
-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.py255
-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.conf31
-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.conf29
-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.conf29
-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.conf28
-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.conf28
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt5/zebra.conf22
-rwxr-xr-xtests/topotests/bfd_ospf_topo1/test_bfd_ospf_topo1.py260
-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.py160
-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.py213
-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.py151
-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/bfd-static-down.json12
-rw-r--r--tests/topotests/bfd_topo3/r3/bfd-static.json13
-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/staticd.conf1
-rw-r--r--tests/topotests/bfd_topo3/r3/zebra.conf14
-rw-r--r--tests/topotests/bfd_topo3/r4/bfd-peers.json90
-rw-r--r--tests/topotests/bfd_topo3/r4/bfdd.conf16
-rw-r--r--tests/topotests/bfd_topo3/r4/bgpd.conf19
-rw-r--r--tests/topotests/bfd_topo3/r4/staticd.conf2
-rw-r--r--tests/topotests/bfd_topo3/r4/zebra.conf14
-rw-r--r--tests/topotests/bfd_topo3/r5/bfd-peers.json23
-rw-r--r--tests/topotests/bfd_topo3/r5/bfdd.conf11
-rw-r--r--tests/topotests/bfd_topo3/r5/staticd.conf2
-rw-r--r--tests/topotests/bfd_topo3/r5/zebra.conf10
-rw-r--r--tests/topotests/bfd_topo3/r6/bfd-peers.json46
-rw-r--r--tests/topotests/bfd_topo3/r6/bfd-static-down.json19
-rw-r--r--tests/topotests/bfd_topo3/r6/bfd-static.json19
-rw-r--r--tests/topotests/bfd_topo3/r6/bfdd.conf11
-rw-r--r--tests/topotests/bfd_topo3/r6/staticd.conf5
-rw-r--r--tests/topotests/bfd_topo3/r6/zebra.conf10
-rw-r--r--tests/topotests/bfd_topo3/test_bfd_topo3.dot95
-rw-r--r--tests/topotests/bfd_topo3/test_bfd_topo3.jpgbin0 -> 42789 bytes
-rw-r--r--tests/topotests/bfd_topo3/test_bfd_topo3.py248
-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.py267
-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.py140
-rw-r--r--tests/topotests/bgp_accept_own/__init__.py0
-rw-r--r--tests/topotests/bgp_accept_own/ce1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_accept_own/ce1/zebra.conf9
-rw-r--r--tests/topotests/bgp_accept_own/ce2/bgpd.conf12
-rw-r--r--tests/topotests/bgp_accept_own/ce2/zebra.conf9
-rw-r--r--tests/topotests/bgp_accept_own/pe1/bgpd.conf50
-rw-r--r--tests/topotests/bgp_accept_own/pe1/ldpd.conf12
-rw-r--r--tests/topotests/bgp_accept_own/pe1/ospfd.conf7
-rw-r--r--tests/topotests/bgp_accept_own/pe1/zebra.conf18
-rw-r--r--tests/topotests/bgp_accept_own/rr1/bgpd.conf25
-rw-r--r--tests/topotests/bgp_accept_own/rr1/ldpd.conf12
-rw-r--r--tests/topotests/bgp_accept_own/rr1/ospfd.conf7
-rw-r--r--tests/topotests/bgp_accept_own/rr1/zebra.conf9
-rw-r--r--tests/topotests/bgp_accept_own/test_bgp_accept_own.py216
-rw-r--r--tests/topotests/bgp_addpath_best_selected/__init__.py0
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r1/bgpd.conf7
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r2/bgpd.conf27
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r2/zebra.conf10
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r3/bgpd.conf9
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r3/zebra.conf7
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r4/bgpd.conf9
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r4/zebra.conf7
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r5/bgpd.conf9
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r5/zebra.conf7
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r6/bgpd.conf9
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r6/zebra.conf7
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r7/bgpd.conf7
-rw-r--r--tests/topotests/bgp_addpath_best_selected/r7/zebra.conf4
-rw-r--r--tests/topotests/bgp_addpath_best_selected/test_bgp_addpath_best_selected.py190
-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.py135
-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.py106
-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.py109
-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.py279
-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.py120
-rw-r--r--tests/topotests/bgp_aigp/__init__.py0
-rw-r--r--tests/topotests/bgp_aigp/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_aigp/r1/ospfd.conf17
-rw-r--r--tests/topotests/bgp_aigp/r1/zebra.conf10
-rw-r--r--tests/topotests/bgp_aigp/r2/bgpd.conf15
-rw-r--r--tests/topotests/bgp_aigp/r2/ospfd.conf13
-rw-r--r--tests/topotests/bgp_aigp/r2/zebra.conf10
-rw-r--r--tests/topotests/bgp_aigp/r3/bgpd.conf15
-rw-r--r--tests/topotests/bgp_aigp/r3/ospfd.conf13
-rw-r--r--tests/topotests/bgp_aigp/r3/zebra.conf10
-rw-r--r--tests/topotests/bgp_aigp/r4/bgpd.conf17
-rw-r--r--tests/topotests/bgp_aigp/r4/ospfd.conf13
-rw-r--r--tests/topotests/bgp_aigp/r4/zebra.conf10
-rw-r--r--tests/topotests/bgp_aigp/r5/bgpd.conf17
-rw-r--r--tests/topotests/bgp_aigp/r5/ospfd.conf13
-rw-r--r--tests/topotests/bgp_aigp/r5/zebra.conf10
-rw-r--r--tests/topotests/bgp_aigp/r6/bgpd.conf20
-rw-r--r--tests/topotests/bgp_aigp/r6/ospfd.conf17
-rw-r--r--tests/topotests/bgp_aigp/r6/zebra.conf13
-rw-r--r--tests/topotests/bgp_aigp/r7/bgpd.conf22
-rw-r--r--tests/topotests/bgp_aigp/r7/zebra.conf9
-rw-r--r--tests/topotests/bgp_aigp/test_bgp_aigp.py217
-rw-r--r--tests/topotests/bgp_always_compare_med/bgp_always_compare_med_topo1.json152
-rw-r--r--tests/topotests/bgp_always_compare_med/test_bgp_always_compare_med_topo1.py1118
-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.py957
-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.py109
-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.py102
-rw-r--r--tests/topotests/bgp_asdot_regex/__init__.py0
-rw-r--r--tests/topotests/bgp_asdot_regex/r1/bgpd.conf27
-rw-r--r--tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json80
-rw-r--r--tests/topotests/bgp_asdot_regex/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_asdot_regex/r2/bgpd.conf26
-rw-r--r--tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json80
-rw-r--r--tests/topotests/bgp_asdot_regex/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py122
-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.py100
-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.py266
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth1.py232
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth2.py240
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth3.py221
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth4.py238
-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.py1169
-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.conf11
-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.conf10
-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.py112
-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.conf13
-rw-r--r--tests/topotests/bgp_blackhole_community/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py161
-rw-r--r--tests/topotests/bgp_bmp/__init__.py0
-rw-r--r--tests/topotests/bgp_bmp/r1/bgpd.conf22
-rw-r--r--tests/topotests/bgp_bmp/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_bmp/r2/bgpd.conf19
-rw-r--r--tests/topotests/bgp_bmp/r2/zebra.conf8
-rw-r--r--tests/topotests/bgp_bmp/test_bgp_bmp.py246
-rw-r--r--tests/topotests/bgp_color_extcommunities/__init__.py0
-rw-r--r--tests/topotests/bgp_color_extcommunities/r1/bgpd.conf17
-rw-r--r--tests/topotests/bgp_color_extcommunities/r1/zebra.conf3
-rw-r--r--tests/topotests/bgp_color_extcommunities/r2/bgpd.conf4
-rw-r--r--tests/topotests/bgp_color_extcommunities/r2/zebra.conf4
-rw-r--r--tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py125
-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.py105
-rw-r--r--tests/topotests/bgp_comm_list_match/__init__.py0
-rw-r--r--tests/topotests/bgp_comm_list_match/r1/bgpd.conf28
-rw-r--r--tests/topotests/bgp_comm_list_match/r1/zebra.conf12
-rw-r--r--tests/topotests/bgp_comm_list_match/r2/bgpd.conf24
-rw-r--r--tests/topotests/bgp_comm_list_match/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_comm_list_match/r3/bgpd.conf21
-rw-r--r--tests/topotests/bgp_comm_list_match/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py138
-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.py631
-rw-r--r--tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py371
-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.py140
-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.py209
-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.py1297
-rw-r--r--tests/topotests/bgp_conditional_advertisement_static_route/__init__.py0
-rw-r--r--tests/topotests/bgp_conditional_advertisement_static_route/r1/frr.conf10
-rw-r--r--tests/topotests/bgp_conditional_advertisement_static_route/r2/frr.conf40
-rw-r--r--tests/topotests/bgp_conditional_advertisement_static_route/r3/frr.conf19
-rw-r--r--tests/topotests/bgp_conditional_advertisement_static_route/test_bgp_conditional_advertisement_static_route.py138
-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.py141
-rw-r--r--tests/topotests/bgp_confed1/__init__.py0
-rw-r--r--tests/topotests/bgp_confed1/r1/bgp_ipv4_unicast.json63
-rw-r--r--tests/topotests/bgp_confed1/r1/bgp_summary.json13
-rw-r--r--tests/topotests/bgp_confed1/r1/bgpd.conf13
-rw-r--r--tests/topotests/bgp_confed1/r1/isisd.conf0
-rw-r--r--tests/topotests/bgp_confed1/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_confed1/r2/bgp_ipv4_unicast.json63
-rw-r--r--tests/topotests/bgp_confed1/r2/bgp_summary.json18
-rw-r--r--tests/topotests/bgp_confed1/r2/bgpd.conf18
-rw-r--r--tests/topotests/bgp_confed1/r2/isisd.conf8
-rw-r--r--tests/topotests/bgp_confed1/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_confed1/r3/bgp_ipv4_unicast.json77
-rw-r--r--tests/topotests/bgp_confed1/r3/bgp_summary.json18
-rw-r--r--tests/topotests/bgp_confed1/r3/bgpd.conf17
-rw-r--r--tests/topotests/bgp_confed1/r3/isisd.conf8
-rw-r--r--tests/topotests/bgp_confed1/r3/zebra.conf10
-rw-r--r--tests/topotests/bgp_confed1/r4/bgp_ipv4_unicast.json77
-rw-r--r--tests/topotests/bgp_confed1/r4/bgp_summary.json13
-rw-r--r--tests/topotests/bgp_confed1/r4/bgpd.conf14
-rw-r--r--tests/topotests/bgp_confed1/r4/isisd.conf0
-rw-r--r--tests/topotests/bgp_confed1/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_confed1/test_bgp_confed1.py116
-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.py145
-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.py1808
-rw-r--r--tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_1.py2527
-rw-r--r--tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_2.py2427
-rw-r--r--tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_3.py2528
-rw-r--r--tests/topotests/bgp_default_originate/test_default_orginate_vrf.py1377
-rw-r--r--tests/topotests/bgp_default_originate/test_default_originate_conditional_routemap.py2092
-rw-r--r--tests/topotests/bgp_default_originate_timer/__init__.py0
-rw-r--r--tests/topotests/bgp_default_originate_timer/r1/bgpd.conf18
-rw-r--r--tests/topotests/bgp_default_originate_timer/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_default_originate_timer/r2/bgpd.conf6
-rw-r--r--tests/topotests/bgp_default_originate_timer/r2/zebra.conf4
-rw-r--r--tests/topotests/bgp_default_originate_timer/r3/bgpd.conf12
-rw-r--r--tests/topotests/bgp_default_originate_timer/r3/zebra.conf7
-rw-r--r--tests/topotests/bgp_default_originate_timer/test_bgp_default_originate_timer.py123
-rw-r--r--tests/topotests/bgp_default_originate_withdraw/__init__.py0
-rw-r--r--tests/topotests/bgp_default_originate_withdraw/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_default_originate_withdraw/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_default_originate_withdraw/r2/bgpd.conf9
-rw-r--r--tests/topotests/bgp_default_originate_withdraw/r2/zebra.conf4
-rw-r--r--tests/topotests/bgp_default_originate_withdraw/r3/bgpd.conf9
-rw-r--r--tests/topotests/bgp_default_originate_withdraw/r3/zebra.conf5
-rw-r--r--tests/topotests/bgp_default_originate_withdraw/test_bgp_default_originate_withdraw.py159
-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.py102
-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.py97
-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.py121
-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.py107
-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.py99
-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.py129
-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.json404
-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.py1269
-rwxr-xr-xtests/topotests/bgp_distance_change/test_bgp_admin_dist_vrf.py887
-rw-r--r--tests/topotests/bgp_distance_change/test_bgp_distance_change.py120
-rw-r--r--tests/topotests/bgp_dont_capability_negotiate/__init__.py0
-rw-r--r--tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_dont_capability_negotiate/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_dont_capability_negotiate/r2/bgpd.conf9
-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.py156
-rw-r--r--tests/topotests/bgp_dynamic_capability/__init__.py0
-rw-r--r--tests/topotests/bgp_dynamic_capability/r1/frr.conf15
-rw-r--r--tests/topotests/bgp_dynamic_capability/r2/frr.conf22
-rw-r--r--tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py213
-rw-r--r--tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py138
-rw-r--r--tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py161
-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.py112
-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.py158
-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.py170
-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.py746
-rw-r--r--tests/topotests/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py747
-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/leaf1/evpn.conf21
-rw-r--r--tests/topotests/bgp_evpn_mh/leaf1/pim.conf26
-rw-r--r--tests/topotests/bgp_evpn_mh/leaf1/zebra.conf30
-rw-r--r--tests/topotests/bgp_evpn_mh/leaf2/evpn.conf21
-rw-r--r--tests/topotests/bgp_evpn_mh/leaf2/pim.conf20
-rw-r--r--tests/topotests/bgp_evpn_mh/leaf2/zebra.conf24
-rw-r--r--tests/topotests/bgp_evpn_mh/spine1/evpn.conf13
-rw-r--r--tests/topotests/bgp_evpn_mh/spine1/pim.conf18
-rw-r--r--tests/topotests/bgp_evpn_mh/spine1/zebra.conf11
-rw-r--r--tests/topotests/bgp_evpn_mh/spine2/evpn.conf13
-rw-r--r--tests/topotests/bgp_evpn_mh/spine2/pim.conf13
-rw-r--r--tests/topotests/bgp_evpn_mh/spine2/zebra.conf13
-rw-r--r--tests/topotests/bgp_evpn_mh/test_evpn_mh.py819
-rw-r--r--tests/topotests/bgp_evpn_mh/torm11/evpn.conf21
-rw-r--r--tests/topotests/bgp_evpn_mh/torm11/pim.conf19
-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.conf19
-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.conf19
-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.conf19
-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.py401
-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.py223
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/ospfd.conf13
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/zebra.conf7
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgp.l2vpn.evpn.vni.json19
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgpd.conf18
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/evpn.vni.json17
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/ospfd.conf9
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/zebra.conf8
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgp.l2vpn.evpn.vni.json19
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgpd.conf18
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/evpn.vni.json16
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/ospfd.conf9
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/zebra.conf6
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/ospfd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/zebra.conf3
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/ospfd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/zebra.conf3
-rwxr-xr-xtests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/test_bgp_evpn_vxlan_macvrf_soo.py839
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/ospfd.conf13
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/zebra.conf7
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf19
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/evpn.vni.json16
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/ospfd.conf9
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf13
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/bgpd.conf24
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/evpn.vni.json15
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/ospfd.conf9
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/zebra.conf17
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/__init__.py0
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/ospfd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/zebra.conf3
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/ospfd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/zebra.conf3
-rwxr-xr-xtests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py556
-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.json17
-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.json16
-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.py436
-rw-r--r--tests/topotests/bgp_extcomm_list_delete/__init__.py0
-rw-r--r--tests/topotests/bgp_extcomm_list_delete/r1/bgpd.conf20
-rw-r--r--tests/topotests/bgp_extcomm_list_delete/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_extcomm_list_delete/r2/bgpd.conf10
-rw-r--r--tests/topotests/bgp_extcomm_list_delete/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_extcomm_list_delete/test_bgp_extcomm-list_delete.py162
-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.py93
-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.conf29
-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.cfg33
-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.py193
-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.py1527
-rw-r--r--tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py391
-rw-r--r--tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py2720
-rw-r--r--tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py1685
-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.py1497
-rw-r--r--tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py1194
-rw-r--r--tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py1346
-rw-r--r--tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py1009
-rw-r--r--tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.json222
-rw-r--r--tests/topotests/bgp_gr_functionality_topo3/test_bgp_gr_functionality_topo3.py541
-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.py211
-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.conf5
-rw-r--r--tests/topotests/bgp_gr_restart_retain_routes/test_bgp_gr_restart_retain_routes.py109
-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.py343
-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.py593
-rw-r--r--tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py640
-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.py86
-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.py114
-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.py950
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py617
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py766
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py975
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py311
-rw-r--r--tests/topotests/bgp_ipv6_ll_peering/__init__.py0
-rw-r--r--tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_ipv6_ll_peering/r2/bgpd.conf6
-rw-r--r--tests/topotests/bgp_ipv6_ll_peering/r2/zebra.conf4
-rw-r--r--tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py88
-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.py135
-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.py133
-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.py64
-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.py216
-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.py878
-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.py253
-rwxr-xr-xtests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py115
-rw-r--r--tests/topotests/bgp_labeled_unicast_addpath/__init__.py0
-rw-r--r--tests/topotests/bgp_labeled_unicast_addpath/r1/bgpd.conf14
-rw-r--r--tests/topotests/bgp_labeled_unicast_addpath/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_labeled_unicast_addpath/r2/bgpd.conf14
-rw-r--r--tests/topotests/bgp_labeled_unicast_addpath/r2/zebra.conf7
-rw-r--r--tests/topotests/bgp_labeled_unicast_addpath/r3/bgpd.conf35
-rw-r--r--tests/topotests/bgp_labeled_unicast_addpath/r3/zebra.conf13
-rw-r--r--tests/topotests/bgp_labeled_unicast_addpath/r4/bgpd.conf10
-rw-r--r--tests/topotests/bgp_labeled_unicast_addpath/r4/zebra.conf4
-rw-r--r--tests/topotests/bgp_labeled_unicast_addpath/r5/bgpd.conf14
-rw-r--r--tests/topotests/bgp_labeled_unicast_addpath/r5/zebra.conf7
-rw-r--r--tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py136
-rw-r--r--tests/topotests/bgp_labeled_unicast_default_originate/__init__.py0
-rw-r--r--tests/topotests/bgp_labeled_unicast_default_originate/r1/bgpd.conf35
-rw-r--r--tests/topotests/bgp_labeled_unicast_default_originate/r1/zebra.conf5
-rw-r--r--tests/topotests/bgp_labeled_unicast_default_originate/r2/bgpd.conf19
-rw-r--r--tests/topotests/bgp_labeled_unicast_default_originate/r2/zebra.conf5
-rw-r--r--tests/topotests/bgp_labeled_unicast_default_originate/test_bgp_labeled_unicast_default_originate.py130
-rw-r--r--tests/topotests/bgp_large_comm_list_match/__init__.py0
-rw-r--r--tests/topotests/bgp_large_comm_list_match/r1/bgpd.conf28
-rw-r--r--tests/topotests/bgp_large_comm_list_match/r1/zebra.conf12
-rw-r--r--tests/topotests/bgp_large_comm_list_match/r2/bgpd.conf24
-rw-r--r--tests/topotests/bgp_large_comm_list_match/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_large_comm_list_match/r3/bgpd.conf21
-rw-r--r--tests/topotests/bgp_large_comm_list_match/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_large_comm_list_match/test_bgp_large_comm_list_match.py145
-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.py1216
-rw-r--r--tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py2220
-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.py575
-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.py136
-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.py185
-rw-r--r--tests/topotests/bgp_local_as/__init__.py0
-rw-r--r--tests/topotests/bgp_local_as/r1/bgpd.conf14
-rw-r--r--tests/topotests/bgp_local_as/r1/zebra.conf10
-rw-r--r--tests/topotests/bgp_local_as/r2/bgpd.conf6
-rw-r--r--tests/topotests/bgp_local_as/r2/zebra.conf4
-rw-r--r--tests/topotests/bgp_local_as/r3/bgpd.conf6
-rw-r--r--tests/topotests/bgp_local_as/r3/zebra.conf4
-rw-r--r--tests/topotests/bgp_local_as/test_bgp_local_as.py121
-rw-r--r--tests/topotests/bgp_local_as_dotplus_private_remove/__init__.py0
-rw-r--r--tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf5
-rw-r--r--tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_local_as_dotplus_private_remove/r3/bgpd.conf8
-rw-r--r--tests/topotests/bgp_local_as_dotplus_private_remove/r3/zebra.conf9
-rw-r--r--tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf5
-rw-r--r--tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py129
-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.py116
-rw-r--r--tests/topotests/bgp_local_asn/bgp_local_asn_agg.json147
-rw-r--r--tests/topotests/bgp_local_asn/bgp_local_asn_ecmp.json317
-rw-r--r--tests/topotests/bgp_local_asn/bgp_local_asn_topo1.json132
-rw-r--r--tests/topotests/bgp_local_asn/bgp_local_asn_topo2.json117
-rw-r--r--tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo1.json152
-rw-r--r--tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo2.json128
-rw-r--r--tests/topotests/bgp_local_asn/test_bgp_local_asn_agg.py407
-rw-r--r--tests/topotests/bgp_local_asn/test_bgp_local_asn_ecmp.py511
-rw-r--r--tests/topotests/bgp_local_asn/test_bgp_local_asn_topo1.py3642
-rw-r--r--tests/topotests/bgp_local_asn/test_bgp_local_asn_topo2.py686
-rw-r--r--tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo1.py1095
-rw-r--r--tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo2.py813
-rw-r--r--tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json147
-rw-r--r--tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json317
-rw-r--r--tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json132
-rw-r--r--tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py420
-rw-r--r--tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py524
-rw-r--r--tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py3655
-rw-r--r--tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf15
-rw-r--r--tests/topotests/bgp_lu_explicitnull/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf15
-rw-r--r--tests/topotests/bgp_lu_explicitnull/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py196
-rw-r--r--tests/topotests/bgp_lu_topo1/R1/bgpd.conf21
-rw-r--r--tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json6
-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.json6
-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.py168
-rw-r--r--tests/topotests/bgp_lu_topo2/R1/bgpd.conf29
-rw-r--r--tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json6
-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.json6
-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.py208
-rw-r--r--tests/topotests/bgp_max_med_on_startup/__init__.py0
-rw-r--r--tests/topotests/bgp_max_med_on_startup/r1/bgpd.conf11
-rw-r--r--tests/topotests/bgp_max_med_on_startup/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_max_med_on_startup/r2/bgpd.conf7
-rw-r--r--tests/topotests/bgp_max_med_on_startup/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_max_med_on_startup/test_bgp_max_med_on_startup.py101
-rw-r--r--tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py0
-rw-r--r--tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf13
-rw-r--r--tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf10
-rw-r--r--tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf9
-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.py94
-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.py187
-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.py85
-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.py6277
-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.py3839
-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.py225
-rw-r--r--tests/topotests/bgp_node_target_extcommunities/__init__.py0
-rw-r--r--tests/topotests/bgp_node_target_extcommunities/r1/frr.conf21
-rw-r--r--tests/topotests/bgp_node_target_extcommunities/r2/frr.conf8
-rw-r--r--tests/topotests/bgp_node_target_extcommunities/r3/frr.conf8
-rw-r--r--tests/topotests/bgp_node_target_extcommunities/r4/frr.conf8
-rw-r--r--tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py132
-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.py146
-rw-r--r--tests/topotests/bgp_path_attribute_discard/__init__.py0
-rw-r--r--tests/topotests/bgp_path_attribute_discard/exabgp.env53
-rw-r--r--tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg24
-rw-r--r--tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_path_attribute_discard/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py154
-rw-r--r--tests/topotests/bgp_path_attribute_treat_as_withdraw/__init__.py0
-rw-r--r--tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/bgpd.conf14
-rw-r--r--tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/bgpd.conf6
-rw-r--r--tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/zebra.conf4
-rw-r--r--tests/topotests/bgp_path_attribute_treat_as_withdraw/test_bgp_path_attribute_treat_as_withdraw.py149
-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.py1528
-rw-r--r--tests/topotests/bgp_path_selection/__init__.py0
-rw-r--r--tests/topotests/bgp_path_selection/r1/bgpd.conf28
-rw-r--r--tests/topotests/bgp_path_selection/r1/ldpd.conf26
-rw-r--r--tests/topotests/bgp_path_selection/r1/staticd.conf2
-rw-r--r--tests/topotests/bgp_path_selection/r1/zebra.conf11
-rw-r--r--tests/topotests/bgp_path_selection/r2/bgpd.conf25
-rw-r--r--tests/topotests/bgp_path_selection/r2/ldpd.conf26
-rw-r--r--tests/topotests/bgp_path_selection/r2/staticd.conf0
-rw-r--r--tests/topotests/bgp_path_selection/r2/zebra.conf7
-rw-r--r--tests/topotests/bgp_path_selection/r3/bgpd.conf25
-rw-r--r--tests/topotests/bgp_path_selection/r3/ldpd.conf24
-rw-r--r--tests/topotests/bgp_path_selection/r3/staticd.conf0
-rw-r--r--tests/topotests/bgp_path_selection/r3/zebra.conf7
-rw-r--r--tests/topotests/bgp_path_selection/test_bgp_path_selection.py220
-rw-r--r--tests/topotests/bgp_peer_graceful_shutdown/__init__.py0
-rw-r--r--tests/topotests/bgp_peer_graceful_shutdown/r1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_peer_graceful_shutdown/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_peer_graceful_shutdown/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_peer_graceful_shutdown/r2/zebra.conf7
-rw-r--r--tests/topotests/bgp_peer_graceful_shutdown/r3/bgpd.conf5
-rw-r--r--tests/topotests/bgp_peer_graceful_shutdown/r3/zebra.conf4
-rw-r--r--tests/topotests/bgp_peer_graceful_shutdown/test_bgp_peer_graceful_shutdown.py107
-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.conf11
-rw-r--r--tests/topotests/bgp_peer_group/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_peer_group/test_bgp_peer-group.py101
-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.py372
-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/prefix_modify.json120
-rw-r--r--tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py1341
-rw-r--r--tests/topotests/bgp_prefix_list_topo1/test_prefix_modify.py404
-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.py172
-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.py103
-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.py2407
-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.py138
-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.py415
-rw-r--r--tests/topotests/bgp_remove_private_as_route_map/__init__.py0
-rw-r--r--tests/topotests/bgp_remove_private_as_route_map/r1/frr.conf10
-rw-r--r--tests/topotests/bgp_remove_private_as_route_map/r2/frr.conf19
-rw-r--r--tests/topotests/bgp_remove_private_as_route_map/test_bgp_remove_private_as_route_map.py90
-rwxr-xr-xtests/topotests/bgp_rfapi_basic_sanity/__init__.py0
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/customize.py102
-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.py84
-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.py122
-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.py172
-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.py131
-rw-r--r--tests/topotests/bgp_route_aggregation/bgp_aggregation.json249
-rw-r--r--tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py1160
-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.py1393
-rw-r--r--tests/topotests/bgp_route_map/test_route_map_topo2.py3958
-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.py108
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/__init__.py0
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf32
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf10
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf10
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py115
-rw-r--r--tests/topotests/bgp_route_map_on_match_next/__init__.py0
-rw-r--r--tests/topotests/bgp_route_map_on_match_next/r1/bgpd.conf10
-rw-r--r--tests/topotests/bgp_route_map_on_match_next/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_route_map_on_match_next/r2/bgpd.conf17
-rw-r--r--tests/topotests/bgp_route_map_on_match_next/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_route_map_on_match_next/test_bgp_route_map_on_match_next.py111
-rw-r--r--tests/topotests/bgp_route_map_vpn_import/__init__.py0
-rw-r--r--tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf46
-rw-r--r--tests/topotests/bgp_route_map_vpn_import/r1/zebra.conf16
-rw-r--r--tests/topotests/bgp_route_map_vpn_import/test_bgp_route_map_vpn_import.py113
-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.py130
-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.py177
-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.conf19
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf10
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf10
-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.py134
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/__init__.py0
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf17
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf9
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf10
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py145
-rw-r--r--tests/topotests/bgp_set_aspath_replace/__init__.py0
-rw-r--r--tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf18
-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.py201
-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.py117
-rw-r--r--tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf31
-rw-r--r--tests/topotests/bgp_snmp_bgp4v2mib/r1/zebra.conf5
-rw-r--r--tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf23
-rw-r--r--tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf17
-rw-r--r--tests/topotests/bgp_snmp_bgp4v2mib/r2/zebra.conf5
-rwxr-xr-xtests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py236
-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.py738
-rw-r--r--tests/topotests/bgp_software_version/__init__.py0
-rw-r--r--tests/topotests/bgp_software_version/r1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_software_version/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_software_version/r2/bgpd.conf7
-rw-r--r--tests/topotests/bgp_software_version/r2/zebra.conf4
-rw-r--r--tests/topotests/bgp_software_version/test_bgp_software_version.py98
-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.py173
-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/c31/bgpd.conf0
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/zebra.conf6
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/bgpd.conf0
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/zebra.conf6
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf57
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/zebra.conf32
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf52
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/zebra.conf29
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf52
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/staticd.conf6
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/zebra.conf29
-rwxr-xr-xtests/topotests/bgp_srv6l3vpn_over_ipv6/test_bgp_srv6l3vpn_over_ipv6.py118
-rw-r--r--tests/topotests/bgp_srv6l3vpn_route_leak/ce1/bgpd.conf0
-rw-r--r--tests/topotests/bgp_srv6l3vpn_route_leak/ce1/zebra.conf9
-rw-r--r--tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf41
-rw-r--r--tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/default_ipv4_vpn.json31
-rw-r--r--tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf10_ipv4_unicast.json25
-rw-r--r--tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json22
-rw-r--r--tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4_unicast.json27
-rw-r--r--tests/topotests/bgp_srv6l3vpn_route_leak/pe1/zebra.conf27
-rwxr-xr-xtests/topotests/bgp_srv6l3vpn_route_leak/test_bgp_srv6l3vpn_route_leak.py115
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf16
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf16
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf79
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json169
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json23
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json53
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json23
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json54
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json77
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json107
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json77
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json106
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json54
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json53
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json106
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json106
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json77
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json22
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json112
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json106
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf43
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf80
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json169
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json106
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json112
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf43
-rwxr-xr-xtests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py438
-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.py276
-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.conf68
-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.conf68
-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.py146
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/ip_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/zebra.conf16
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/ip_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/zebra.conf16
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/ip_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/zebra.conf16
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/ip_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/zebra.conf16
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/ip_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/zebra.conf16
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/ip_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/zebra.conf16
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf87
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json167
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_deleted.json90
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_recreated.json166
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json115
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json167
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json170
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_locator_deleted.json160
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_locator_recreated.json169
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json116
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json170
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v4_rib.json86
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v6_rib.json86
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v4_rib.json92
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v6_rib.json92
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf43
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf88
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json167
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_deleted.json90
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_recreated.json166
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json117
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json167
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json170
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_locator_deleted.json93
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_locator_recreated.json169
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json120
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json170
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v4_rib.json92
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v6_rib.json92
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v4_rib.json86
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v6_rib.json86
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf43
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/test_bgp_srv6l3vpn_to_bgp_vrf3.py337
-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.py237
-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.py162
-rw-r--r--tests/topotests/bgp_tcp_mss/test_bgp_vrf_tcp_mss.py753
-rw-r--r--tests/topotests/bgp_tcp_mss_passive/__init__.py0
-rw-r--r--tests/topotests/bgp_tcp_mss_passive/r1/frr.conf12
-rw-r--r--tests/topotests/bgp_tcp_mss_passive/r2/frr.conf10
-rw-r--r--tests/topotests/bgp_tcp_mss_passive/test_bgp_tcp_mss_passive.py106
-rw-r--r--tests/topotests/bgp_unique_rid/bgp_unique_rid.json505
-rw-r--r--tests/topotests/bgp_unique_rid/bgp_unique_rid_vrf.json529
-rw-r--r--tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py891
-rw-r--r--tests/topotests/bgp_unique_rid/test_bgp_unique_rid_vrf.py466
-rw-r--r--tests/topotests/bgp_unnumbered/__init__.py0
-rw-r--r--tests/topotests/bgp_unnumbered/r1/bgpd.conf9
-rw-r--r--tests/topotests/bgp_unnumbered/r1/zebra.conf10
-rw-r--r--tests/topotests/bgp_unnumbered/r2/bgpd.conf9
-rw-r--r--tests/topotests/bgp_unnumbered/r2/zebra.conf12
-rw-r--r--tests/topotests/bgp_unnumbered/test_bgp_unnumbered.py119
-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.conf16
-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.py295
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/__init__.py0
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/cpe1/bgpd.conf9
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/cpe1/zebra.conf9
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/cpe2/bgpd.conf6
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/cpe2/zebra.conf6
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe1/bgpd.conf38
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe1/ldpd.conf10
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe1/ospf6d.conf12
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe1/zebra.conf14
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe2/bgpd.conf29
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe2/ldpd.conf10
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe2/ospf6d.conf12
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/pe2/zebra.conf14
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py171
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/__init__.py0
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/h1/zebra.conf7
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/h2/zebra.conf6
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/h3/zebra.conf6
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/r1/bgp_ipv4_routes.json49
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf29
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/r1/zebra.conf10
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf31
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/r2/ipv4_vpn_summary.json24
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/r2/zebra.conf13
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf25
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/r3/zebra.conf14
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/rr100/bgpd.conf29
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/rr100/zebra.conf7
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/rs200/bgpd.conf19
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/rs200/zebra.conf4
-rw-r--r--tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py917
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/__init__.py0
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r1/bgp_ipv4_routes.json49
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf28
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json62
-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/r3/bgp_ipv4_routes.json38
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r3/bgpd.conf25
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r3/zebra.conf7
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py228
-rw-r--r--tests/topotests/bgp_vpnv4_gre/__init__.py0
-rw-r--r--tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf27
-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.py178
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/__init__.py0
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf46
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_all.json175
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init.json121
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json148
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json148
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_init.json156
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r1_vrf1.json190
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf2.json188
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf3.json188
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf16
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf54
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_routes_all.json177
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_summary.json17
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf22
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py567
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/__init__.py0
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgp_ipv4_routes_vrf1.json143
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgpd.conf30
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r1/ipv4_routes.json50
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r1/zebra.conf18
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r11/bgpd.conf11
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r11/zebra.conf4
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r12/bgpd.conf9
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r12/zebra.conf4
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r13/bgpd.conf9
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r13/zebra.conf4
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_ipv4_routes.json38
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_vpnv4_routes.json187
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgpd.conf25
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/r2/zebra.conf7
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/rr/bgpd.conf13
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/rr/zebra.conf4
-rw-r--r--tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py806
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/__init__.py0
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json186
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf45
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r1/zebra.conf18
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf15
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r11/zebra.conf4
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r12/bgpd.conf13
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r12/zebra.conf4
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf12
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r13/zebra.conf4
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgp_vpnv6_routes.json187
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgpd.conf25
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r2/zebra.conf7
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/rr/bgpd.conf24
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/rr/zebra.conf4
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py808
-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.py1903
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py890
-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.py1790
-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-1.py394
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py919
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py919
-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.py108
-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.py91
-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.py879
-rw-r--r--tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo2.py526
-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.py155
-rw-r--r--tests/topotests/bgp_vrf_md5_peering/__init__.py0
-rw-r--r--tests/topotests/bgp_vrf_md5_peering/exabgp.env53
-rw-r--r--tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg13
-rw-r--r--tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf11
-rw-r--r--tests/topotests/bgp_vrf_md5_peering/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_vrf_md5_peering/test_bgp_vrf_md5_peering.py87
-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.py212
-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.py136
-rw-r--r--tests/topotests/config_timing/r1/staticd.conf1
-rw-r--r--tests/topotests/config_timing/r1/zebra.conf16
-rw-r--r--tests/topotests/config_timing/test_config_timing.py262
-rwxr-xr-xtests/topotests/conftest.py691
-rw-r--r--tests/topotests/cspf_topo1/r1/isisd.conf35
-rw-r--r--tests/topotests/cspf_topo1/r1/sharpd.conf2
-rw-r--r--tests/topotests/cspf_topo1/r1/zebra.conf28
-rw-r--r--tests/topotests/cspf_topo1/r2/isisd.conf50
-rw-r--r--tests/topotests/cspf_topo1/r2/zebra.conf46
-rw-r--r--tests/topotests/cspf_topo1/r3/isisd.conf36
-rw-r--r--tests/topotests/cspf_topo1/r3/zebra.conf27
-rw-r--r--tests/topotests/cspf_topo1/r4/isisd.conf42
-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.py304
-rw-r--r--tests/topotests/docker/README.md72
-rwxr-xr-xtests/topotests/docker/build.sh11
-rwxr-xr-xtests/topotests/docker/frr-topotests.sh155
-rwxr-xr-xtests/topotests/docker/inner/compile_frr.sh87
-rwxr-xr-xtests/topotests/docker/inner/entrypoint.sh31
-rwxr-xr-xtests/topotests/docker/inner/funcs.sh53
-rw-r--r--tests/topotests/docker/inner/motd.txt15
-rwxr-xr-xtests/topotests/docker/inner/openvswitch.sh40
-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.py279
-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.json39
-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.py204
-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.py1007
-rw-r--r--tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py2322
-rw-r--r--tests/topotests/example_munet/munet.yaml17
-rw-r--r--tests/topotests/example_munet/r1/daemons6
-rw-r--r--tests/topotests/example_munet/r1/frr.conf7
-rw-r--r--tests/topotests/example_munet/r1/vtysh.conf1
-rw-r--r--tests/topotests/example_munet/r2/daemons6
-rw-r--r--tests/topotests/example_munet/r2/frr.conf10
-rw-r--r--tests/topotests/example_munet/r2/vtysh.conf1
-rw-r--r--tests/topotests/example_munet/r3/daemons6
-rw-r--r--tests/topotests/example_munet/r3/frr.conf7
-rw-r--r--tests/topotests/example_munet/r3/vtysh.conf1
-rw-r--r--tests/topotests/example_munet/test_munet.py10
-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.py160
-rw-r--r--tests/topotests/example_test/test_template_json.json188
-rw-r--r--tests/topotests/example_test/test_template_json.py55
-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.py184
-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.py184
-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.py195
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.py166
-rw-r--r--tests/topotests/isis_advertise_high_metrics/__init__.py0
-rw-r--r--tests/topotests/isis_advertise_high_metrics/r1/isisd.conf19
-rw-r--r--tests/topotests/isis_advertise_high_metrics/r1/zebra.conf5
-rw-r--r--tests/topotests/isis_advertise_high_metrics/r2/isisd.conf18
-rw-r--r--tests/topotests/isis_advertise_high_metrics/r2/zebra.conf5
-rw-r--r--tests/topotests/isis_advertise_high_metrics/r3/isisd.conf20
-rw-r--r--tests/topotests/isis_advertise_high_metrics/r3/zebra.conf5
-rw-r--r--tests/topotests/isis_advertise_high_metrics/r4/isisd.conf19
-rw-r--r--tests/topotests/isis_advertise_high_metrics/r4/zebra.conf5
-rw-r--r--tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py473
-rw-r--r--tests/topotests/isis_lfa_topo1/__init__.py0
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/bfdd.conf4
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/isisd.conf57
-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/step14/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step15/show_ipv6_route.ref.diff50
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step16/show_ipv6_route.ref.diff53
-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/bfdd.conf4
-rw-r--r--tests/topotests/isis_lfa_topo1/rt2/isisd.conf42
-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.conf41
-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.conf34
-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.conf34
-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.conf33
-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.conf56
-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.py1086
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/__init__.py0
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt1/isisd.conf28
-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.conf37
-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.conf37
-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.conf45
-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.conf45
-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.conf34
-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.py364
-rw-r--r--tests/topotests/isis_rlfa_topo1/__init__.py0
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/isisd.conf41
-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.conf34
-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.conf34
-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.conf34
-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.conf34
-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.conf34
-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.conf34
-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.conf34
-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.py644
-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.conf25
-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_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_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_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_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.py361
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf96
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref126
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref126
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref126
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref115
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref450
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref126
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref126
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref112
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref450
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref108
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref450
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref126
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref126
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf39
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf96
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref114
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref450
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref111
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref450
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref107
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref450
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref482
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf39
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf76
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref114
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref450
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref111
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref450
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref107
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref450
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref514
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref125
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref482
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf39
-rwxr-xr-xtests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py631
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/README.md8
-rwxr-xr-xtests/topotests/isis_sr_flex_algo_topo2/configure-te.sh46
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf17
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf56
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf20
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json438
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf34
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf60
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json428
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf40
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf54
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json408
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf37
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf60
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json428
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf40
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf52
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json286
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf31
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf60
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json428
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf40
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf54
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json408
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf37
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf60
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json428
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf40
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf50
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json286
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf29
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf17
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf56
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf20
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json438
-rw-r--r--tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf34
-rwxr-xr-xtests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py187
-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.py857
-rw-r--r--tests/topotests/isis_sr_topo1/__init__.py0
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/isisd.conf32
-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.conf45
-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.conf45
-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.conf55
-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.conf55
-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.conf39
-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.py1073
-rw-r--r--tests/topotests/isis_srv6_topo1/dst/sharpd.conf0
-rw-r--r--tests/topotests/isis_srv6_topo1/dst/zebra.conf22
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/isisd.conf35
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/sharpd.conf0
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step1/show_ip_route.ref276
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step1/show_ipv6_route.ref270
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step1/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref32
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step2/show_ip_route.ref276
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step2/show_ipv6_route.ref204
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step2/show_srv6_locator_table.ref2
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step2/show_yang_interface_isis_adjacencies.ref32
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step3/show_ip_route.ref276
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step3/show_ipv6_route.ref270
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step3/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step3/show_yang_interface_isis_adjacencies.ref32
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step4/show_ip_route.ref276
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step4/show_ipv6_route.ref204
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step4/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step4/show_yang_interface_isis_adjacencies.ref32
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step5/show_ip_route.ref276
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step5/show_ipv6_route.ref270
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step5/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step5/show_yang_interface_isis_adjacencies.ref32
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step6/show_ip_route.ref276
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step6/show_ipv6_route.ref204
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step6/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step6/show_yang_interface_isis_adjacencies.ref32
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step7/show_ip_route.ref276
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step7/show_ipv6_route.ref270
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step7/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step7/show_yang_interface_isis_adjacencies.ref32
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step8/show_ip_route.ref276
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step8/show_ipv6_route.ref204
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step8/show_srv6_locator_table.ref2
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step8/show_yang_interface_isis_adjacencies.ref32
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step9/show_ip_route.ref276
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step9/show_ipv6_route.ref270
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step9/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/step9/show_yang_interface_isis_adjacencies.ref32
-rw-r--r--tests/topotests/isis_srv6_topo1/rt1/zebra.conf28
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/isisd.conf48
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step1/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step1/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step1/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step2/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step2/show_ipv6_route.ref327
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step2/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step2/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step3/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step3/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step3/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step3/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step4/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step4/show_ipv6_route.ref327
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step4/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step4/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step5/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step5/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step5/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step5/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step6/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step6/show_ipv6_route.ref327
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step6/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step6/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step7/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step7/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step7/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step7/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step8/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step8/show_ipv6_route.ref327
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step8/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step8/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step9/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step9/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step9/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/step9/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt2/zebra.conf34
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/isisd.conf48
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step1/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step1/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step1/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step2/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step2/show_ipv6_route.ref327
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step2/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step2/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step3/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step3/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step3/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step3/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step4/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step4/show_ipv6_route.ref327
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step4/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step4/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step5/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step5/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step5/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step5/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step6/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step6/show_ipv6_route.ref327
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step6/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step6/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step7/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step7/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step7/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step7/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step8/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step8/show_ipv6_route.ref327
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step8/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step8/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step9/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step9/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step9/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/step9/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_srv6_topo1/rt3/zebra.conf33
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/isisd.conf56
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step1/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step1/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step1/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step2/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step2/show_ipv6_route.ref321
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step2/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step2/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step3/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step3/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step3/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step3/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step4/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step4/show_ipv6_route.ref321
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step4/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step4/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step5/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step5/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step5/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step5/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step6/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step6/show_ipv6_route.ref321
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step6/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step6/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step7/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step7/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step7/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step7/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step8/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step8/show_ipv6_route.ref321
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step8/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step8/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step9/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step9/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step9/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/step9/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt4/zebra.conf36
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/isisd.conf56
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step1/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step1/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step1/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step2/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step2/show_ipv6_route.ref321
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step2/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step2/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step3/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step3/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step3/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step3/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step4/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step4/show_ipv6_route.ref321
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step4/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step4/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step5/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step5/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step5/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step5/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step6/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step6/show_ipv6_route.ref321
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step6/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step6/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step7/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step7/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step7/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step7/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step8/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step8/show_ipv6_route.ref321
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step8/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step8/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step9/show_ip_route.ref296
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step9/show_ipv6_route.ref346
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step9/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/step9/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_srv6_topo1/rt5/zebra.conf36
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/isisd.conf42
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/sharpd.conf0
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step1/show_ip_route.ref273
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step1/show_ipv6_route.ref268
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step1/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step2/show_ip_route.ref273
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step2/show_ipv6_route.ref243
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step2/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step2/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step3/show_ip_route.ref273
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step3/show_ipv6_route.ref268
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step3/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step3/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step4/show_ip_route.ref273
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step4/show_ipv6_route.ref243
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step4/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step4/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step5/show_ip_route.ref273
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step5/show_ipv6_route.ref268
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step5/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step5/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step6/show_ip_route.ref273
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step6/show_ipv6_route.ref243
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step6/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step6/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step7/show_ip_route.ref273
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step7/show_ipv6_route.ref268
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step7/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step7/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step8/show_ip_route.ref273
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step8/show_ipv6_route.ref243
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step8/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step8/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step9/show_ip_route.ref273
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step9/show_ipv6_route.ref268
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step9/show_srv6_locator_table.ref19
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/step9/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_srv6_topo1/rt6/zebra.conf36
-rw-r--r--tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py1124
-rwxr-xr-xtests/topotests/isis_te_topo1/__init__.py0
-rw-r--r--tests/topotests/isis_te_topo1/r1/isisd.conf33
-rw-r--r--tests/topotests/isis_te_topo1/r1/zebra.conf26
-rw-r--r--tests/topotests/isis_te_topo1/r2/isisd.conf49
-rw-r--r--tests/topotests/isis_te_topo1/r2/zebra.conf40
-rw-r--r--tests/topotests/isis_te_topo1/r3/isisd.conf36
-rw-r--r--tests/topotests/isis_te_topo1/r3/zebra.conf25
-rw-r--r--tests/topotests/isis_te_topo1/r4/isisd.conf41
-rw-r--r--tests/topotests/isis_te_topo1/r4/zebra.conf22
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step1.json838
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step10.json967
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step2.json642
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step3.json742
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step4.json742
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step5.json938
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step6.json939
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step7.json969
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step8.json969
l---------tests/topotests/isis_te_topo1/reference/ted_step9.json1
-rw-r--r--tests/topotests/isis_te_topo1/test_isis_te_topo1.py306
-rw-r--r--tests/topotests/isis_tilfa_topo1/__init__.py0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/isisd.conf35
-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/step10/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step10/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step10/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step11/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step11/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step11/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step12/show_ip_route.ref.diff19
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step12/show_ipv6_route.ref.diff18
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step12/show_mpls_table.ref.diff28
-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.conf20
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/isisd.conf49
-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/step10/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step10/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step10/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step11/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step11/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step11/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step12/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step12/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step12/show_mpls_table.ref.diff20
-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.conf26
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/isisd.conf49
-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/step10/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step10/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step10/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step11/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step11/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step11/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step12/show_ip_route.ref.diff58
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step12/show_ipv6_route.ref.diff45
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step12/show_mpls_table.ref.diff60
-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.conf26
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/isisd.conf58
-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/step10/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step10/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step10/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step11/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step11/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step11/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step12/show_ip_route.ref.diff144
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step12/show_ipv6_route.ref.diff50
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step12/show_mpls_table.ref.diff78
-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.conf29
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/bfdd.conf14
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/isisd.conf58
-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/step10/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step10/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step10/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step11/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step11/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step11/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step12/show_ip_route.ref.diff151
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step12/show_ipv6_route.ref.diff53
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step12/show_mpls_table.ref.diff80
-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.conf29
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/bfdd.conf14
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/isisd.conf42
-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/step10/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step10/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step10/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step11/show_ip_route.ref.diff125
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step11/show_ipv6_route.ref.diff56
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step11/show_mpls_table.ref.diff106
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step12/show_ip_route.ref.diff153
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step12/show_ipv6_route.ref.diff66
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step12/show_mpls_table.ref.diff78
-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.conf23
-rwxr-xr-xtests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py1113
-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.json165
-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.json80
-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.conf24
-rw-r--r--tests/topotests/isis_topo1/r5/r5_route.json161
-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.conf15
-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.py896
-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.py428
-rw-r--r--tests/topotests/kinds.yaml30
-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.py245
-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.py225
-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.py372
-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.py613
-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.py430
-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.py844
-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.py291
-rw-r--r--tests/topotests/lib/__init__.py0
-rw-r--r--tests/topotests/lib/bgp.py5596
-rw-r--r--tests/topotests/lib/bgprib.py165
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/__init__.py0
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/open/__init__.py34
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/update/__init__.py54
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/update/af.py53
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/update/nlri.py140
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py304
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/update/rd.py59
-rw-r--r--tests/topotests/lib/bmp_collector/bmp.py420
-rwxr-xr-xtests/topotests/lib/bmp_collector/bmpserver45
-rw-r--r--tests/topotests/lib/checkping.py33
-rw-r--r--tests/topotests/lib/common_config.py4960
-rwxr-xr-xtests/topotests/lib/exa-receive.py43
-rw-r--r--tests/topotests/lib/fixtures.py30
-rwxr-xr-xtests/topotests/lib/grpc-query.py128
-rw-r--r--tests/topotests/lib/ltemplate.py307
-rw-r--r--tests/topotests/lib/lutil.py499
-rwxr-xr-xtests/topotests/lib/mcast-tester.py142
-rw-r--r--tests/topotests/lib/micronet.py24
-rw-r--r--tests/topotests/lib/micronet_compat.py370
-rw-r--r--tests/topotests/lib/ospf.py3028
-rw-r--r--tests/topotests/lib/pim.py5203
-rwxr-xr-xtests/topotests/lib/scapy_sendpkt.py44
-rwxr-xr-xtests/topotests/lib/send_bsr_packet.py45
-rw-r--r--tests/topotests/lib/snmptest.py145
-rw-r--r--tests/topotests/lib/test/__init__.py0
-rwxr-xr-xtests/topotests/lib/test/test_json.py627
-rwxr-xr-xtests/topotests/lib/test/test_run_and_expect.py66
-rwxr-xr-xtests/topotests/lib/test/test_version.py77
-rw-r--r--tests/topotests/lib/topogen.py1390
-rw-r--r--tests/topotests/lib/topojson.py405
-rw-r--r--tests/topotests/lib/topolog.py161
-rw-r--r--tests/topotests/lib/topotest.py2397
-rw-r--r--tests/topotests/mgmt_config/r1/early-end-zebra.conf6
-rw-r--r--tests/topotests/mgmt_config/r1/early-end.conf8
-rw-r--r--tests/topotests/mgmt_config/r1/early-end2-zebra.conf7
-rw-r--r--tests/topotests/mgmt_config/r1/early-end2.conf9
-rw-r--r--tests/topotests/mgmt_config/r1/early-exit-zebra.conf6
-rw-r--r--tests/topotests/mgmt_config/r1/early-exit.conf8
-rw-r--r--tests/topotests/mgmt_config/r1/early-exit2-zebra.conf7
-rw-r--r--tests/topotests/mgmt_config/r1/early-exit2.conf9
-rw-r--r--tests/topotests/mgmt_config/r1/frr.conf15
-rw-r--r--tests/topotests/mgmt_config/r1/mgmtd.conf11
-rw-r--r--tests/topotests/mgmt_config/r1/normal-exit.conf8
-rw-r--r--tests/topotests/mgmt_config/r1/one-exit-zebra.conf3
-rw-r--r--tests/topotests/mgmt_config/r1/one-exit.conf3
-rw-r--r--tests/topotests/mgmt_config/r1/one-exit2-zebra.conf4
-rw-r--r--tests/topotests/mgmt_config/r1/one-exit2.conf4
-rw-r--r--tests/topotests/mgmt_config/r1/zebra.conf7
-rw-r--r--tests/topotests/mgmt_config/test_config.py385
-rw-r--r--tests/topotests/mgmt_config/test_regression.py53
-rw-r--r--tests/topotests/mgmt_startup/r1/mgmtd.conf13
-rw-r--r--tests/topotests/mgmt_startup/r1/zebra.conf7
-rw-r--r--tests/topotests/mgmt_startup/r2/staticd.conf7
-rw-r--r--tests/topotests/mgmt_startup/r2/zebra.conf12
-rw-r--r--tests/topotests/mgmt_startup/r3/zebra.conf18
-rw-r--r--tests/topotests/mgmt_startup/r4/frr.conf21
-rw-r--r--tests/topotests/mgmt_startup/test_bigconf.py80
-rw-r--r--tests/topotests/mgmt_startup/test_cfgfile_var.py109
-rw-r--r--tests/topotests/mgmt_startup/test_late_bigconf.py98
-rw-r--r--tests/topotests/mgmt_startup/test_late_uniconf.py44
-rw-r--r--tests/topotests/mgmt_startup/test_latestart.py45
-rw-r--r--tests/topotests/mgmt_startup/util.py98
-rw-r--r--tests/topotests/mgmt_tests/test_yang_mgmt.py548
-rw-r--r--tests/topotests/mgmt_tests/yang_mgmt.json157
-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.py230
-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.py442
-rw-r--r--tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json249
-rw-r--r--tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py926
-rw-r--r--tests/topotests/multicast_pim6_sm_topo1/multicast_pim6_sm_topo1.json300
-rw-r--r--tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py1595
-rw-r--r--tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py591
-rw-r--r--tests/topotests/multicast_pim6_static_rp_topo1/__init__.py0
-rw-r--r--tests/topotests/multicast_pim6_static_rp_topo1/multicast_pim6_static_rp.json197
-rwxr-xr-xtests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py1263
-rwxr-xr-xtests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py1287
-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.py1767
-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.py1102
-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.py1125
-rwxr-xr-xtests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_static_routes_topo1.py924
-rwxr-xr-xtests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py813
-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.py1589
-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.py1825
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json1
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json51
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json1
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json22
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json1
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json61
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json16
-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.py4856
-rwxr-xr-xtests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo4.py1084
-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
-rwxr-xr-xtests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp.py1376
-rwxr-xr-xtests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp1.py1406
-rwxr-xr-xtests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp2.py1783
-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.py3362
-rw-r--r--tests/topotests/multicast_pim_uplink_topo2/multicast_pim_uplink_topo2.json288
-rw-r--r--tests/topotests/multicast_pim_uplink_topo2/test_multicast_pim_uplink_topo2.py1381
-rw-r--r--tests/topotests/multicast_pim_uplink_topo3/multicast_pim_uplink_topo3.json295
-rw-r--r--tests/topotests/multicast_pim_uplink_topo3/test_multicast_pim_uplink_topo3.py940
-rw-r--r--tests/topotests/munet/__init__.py38
-rw-r--r--tests/topotests/munet/__main__.py236
-rw-r--r--tests/topotests/munet/base.py3111
-rw-r--r--tests/topotests/munet/cleanup.py114
-rw-r--r--tests/topotests/munet/cli.py962
-rw-r--r--tests/topotests/munet/compat.py34
-rw-r--r--tests/topotests/munet/config.py213
-rw-r--r--tests/topotests/munet/kinds.yaml84
-rw-r--r--tests/topotests/munet/linux.py267
-rw-r--r--tests/topotests/munet/logconf-mutest.yaml84
-rw-r--r--tests/topotests/munet/logconf.yaml32
-rw-r--r--tests/topotests/munet/mucmd.py111
-rw-r--r--tests/topotests/munet/mulog.py122
-rw-r--r--tests/topotests/munet/munet-schema.json654
-rw-r--r--tests/topotests/munet/mutest/__main__.py445
-rw-r--r--tests/topotests/munet/mutest/userapi.py1111
-rw-r--r--tests/topotests/munet/mutestshare.py254
-rwxr-xr-xtests/topotests/munet/mutini.py432
-rw-r--r--tests/topotests/munet/native.py2941
-rw-r--r--tests/topotests/munet/parser.py374
-rw-r--r--tests/topotests/munet/testing/__init__.py1
-rw-r--r--tests/topotests/munet/testing/fixtures.py447
-rw-r--r--tests/topotests/munet/testing/hooks.py225
-rw-r--r--tests/topotests/munet/testing/util.py110
-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/sharp_route4.json46
-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.py253
-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.py194
-rw-r--r--tests/topotests/ospf6_gr_topo1/__init__.py0
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf29
-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.py583
-rw-r--r--tests/topotests/ospf6_loopback_cost/__init__.py0
-rw-r--r--tests/topotests/ospf6_loopback_cost/r1/frr.conf16
-rw-r--r--tests/topotests/ospf6_loopback_cost/r2/frr.conf16
-rw-r--r--tests/topotests/ospf6_loopback_cost/test_ospf6_loopback_cost.py89
-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.py422
-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.py462
-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.py653
-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_flood_reduction.json214
-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.py3276
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py388
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_authentication.py1333
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_chaos.py513
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py440
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py290
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_flood_reduction.py1066
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_lan.py650
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_nssa.py262
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py699
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py1296
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py778
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_single_area.py988
-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.py118
-rw-r--r--tests/topotests/ospf_gr_helper/ospf_gr_helper.json119
-rw-r--r--tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py380
-rw-r--r--tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py356
-rw-r--r--tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py310
-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.py587
-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.py175
-rwxr-xr-xtests/topotests/ospf_metric_propagation/__init__.py0
-rw-r--r--tests/topotests/ospf_metric_propagation/h1/frr.conf10
-rw-r--r--tests/topotests/ospf_metric_propagation/h2/frr.conf10
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/frr.conf96
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json35
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json35
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json35
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json35
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json35
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json35
-rw-r--r--tests/topotests/ospf_metric_propagation/r2/frr.conf81
-rw-r--r--tests/topotests/ospf_metric_propagation/r3/frr.conf79
-rw-r--r--tests/topotests/ospf_metric_propagation/r4/frr.conf78
-rw-r--r--tests/topotests/ospf_metric_propagation/ra/frr.conf27
-rw-r--r--tests/topotests/ospf_metric_propagation/rb/frr.conf27
-rw-r--r--tests/topotests/ospf_metric_propagation/rc/frr.conf21
-rw-r--r--tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py385
-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.py230
-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.py311
-rw-r--r--tests/topotests/ospf_nssa_topo1/__init__.py0
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf22
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt1/staticd.conf6
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref115
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref115
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref115
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref115
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref103
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref103
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref91
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref103
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref103
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref91
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt1/zebra.conf18
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf35
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt2/staticd.conf6
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref127
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref127
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref139
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref127
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref129
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref117
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref103
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref129
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref129
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref127
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt2/zebra.conf24
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf24
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt3/staticd.conf8
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref138
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref150
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref138
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref138
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref150
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref138
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref126
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref150
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref150
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref150
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt3/zebra.conf18
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf24
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt4/staticd.conf9
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref114
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref126
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref114
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref114
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref126
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref126
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref126
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref126
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref126
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref126
-rw-r--r--tests/topotests/ospf_nssa_topo1/rt4/zebra.conf18
-rw-r--r--tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py416
-rw-r--r--tests/topotests/ospf_prefix_suppression/r1/frr.conf47
-rw-r--r--tests/topotests/ospf_prefix_suppression/r2/frr.conf57
-rw-r--r--tests/topotests/ospf_prefix_suppression/r3/frr.conf25
-rw-r--r--tests/topotests/ospf_prefix_suppression/test_ospf_prefix_suppression.py951
-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.py653
-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.py669
-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.py173
-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.conf22
-rw-r--r--tests/topotests/ospf_te_topo1/r2/ospfd.conf34
-rw-r--r--tests/topotests/ospf_te_topo1/r2/zebra.conf34
-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.py282
-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.py226
-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.py585
-rw-r--r--tests/topotests/ospf_topo2/__init__.py0
-rw-r--r--tests/topotests/ospf_topo2/r1/frr.conf61
-rw-r--r--tests/topotests/ospf_topo2/r2/frr.conf61
-rw-r--r--tests/topotests/ospf_topo2/r3/frr.conf61
-rw-r--r--tests/topotests/ospf_topo2/r4/frr.conf61
-rw-r--r--tests/topotests/ospf_topo2/test_ospf_topo2.dot44
-rw-r--r--tests/topotests/ospf_topo2/test_ospf_topo2.pngbin0 -> 88488 bytes
-rw-r--r--tests/topotests/ospf_topo2/test_ospf_topo2.py317
-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.py152
-rwxr-xr-xtests/topotests/ospfapi/ctester.py154
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.py1586
-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.py2718
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py1414
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py472
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py383
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py158
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py583
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py1229
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py906
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py1266
-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.py435
-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.py332
-rwxr-xr-xtests/topotests/pim_basic/mcast-rx.py79
-rwxr-xr-xtests/topotests/pim_basic/mcast-tx.py84
-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.py235
-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.py225
-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.py389
-rw-r--r--tests/topotests/pytest.ini87
-rw-r--r--tests/topotests/rip_allow_ecmp/__init__.py0
-rw-r--r--tests/topotests/rip_allow_ecmp/r1/frr.conf9
-rw-r--r--tests/topotests/rip_allow_ecmp/r2/frr.conf13
-rw-r--r--tests/topotests/rip_allow_ecmp/r3/frr.conf13
-rw-r--r--tests/topotests/rip_allow_ecmp/r4/frr.conf13
-rw-r--r--tests/topotests/rip_allow_ecmp/r5/frr.conf13
-rw-r--r--tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py156
-rwxr-xr-xtests/topotests/rip_bfd_topo1/__init__.py0
-rw-r--r--tests/topotests/rip_bfd_topo1/r1/bfdd.conf6
-rw-r--r--tests/topotests/rip_bfd_topo1/r1/ripd.conf17
-rw-r--r--tests/topotests/rip_bfd_topo1/r1/zebra.conf11
-rw-r--r--tests/topotests/rip_bfd_topo1/r2/bfdd.conf6
-rw-r--r--tests/topotests/rip_bfd_topo1/r2/ripd.conf11
-rw-r--r--tests/topotests/rip_bfd_topo1/r2/staticd.conf1
-rw-r--r--tests/topotests/rip_bfd_topo1/r2/zebra.conf8
-rw-r--r--tests/topotests/rip_bfd_topo1/r3/bfdd.conf6
-rw-r--r--tests/topotests/rip_bfd_topo1/r3/ripd.conf11
-rw-r--r--tests/topotests/rip_bfd_topo1/r3/staticd.conf1
-rw-r--r--tests/topotests/rip_bfd_topo1/r3/zebra.conf7
-rw-r--r--tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot58
-rw-r--r--tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.pngbin0 -> 27400 bytes
-rw-r--r--tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py252
-rw-r--r--tests/topotests/rip_passive_interface/__init__.py0
-rw-r--r--tests/topotests/rip_passive_interface/r1/frr.conf9
-rw-r--r--tests/topotests/rip_passive_interface/r2/frr.conf13
-rw-r--r--tests/topotests/rip_passive_interface/r3/frr.conf13
-rw-r--r--tests/topotests/rip_passive_interface/test_rip_passive_interface.py102
-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.py336
-rw-r--r--tests/topotests/ripng_allow_ecmp/__init__.py0
-rw-r--r--tests/topotests/ripng_allow_ecmp/r1/frr.conf9
-rw-r--r--tests/topotests/ripng_allow_ecmp/r2/frr.conf13
-rw-r--r--tests/topotests/ripng_allow_ecmp/r3/frr.conf14
-rw-r--r--tests/topotests/ripng_allow_ecmp/r4/frr.conf14
-rw-r--r--tests/topotests/ripng_allow_ecmp/r5/frr.conf14
-rw-r--r--tests/topotests/ripng_allow_ecmp/test_ripng_allow_ecmp.py93
-rw-r--r--tests/topotests/ripng_route_map/__init__.py0
-rw-r--r--tests/topotests/ripng_route_map/r1/frr.conf21
-rw-r--r--tests/topotests/ripng_route_map/r2/frr.conf14
-rw-r--r--tests/topotests/ripng_route_map/r3/frr.conf14
-rw-r--r--tests/topotests/ripng_route_map/test_ripng_route_map.py79
-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.py380
-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.py202
-rw-r--r--tests/topotests/route_scale/test_route_scale1.py64
-rw-r--r--tests/topotests/route_scale/test_route_scale2.py64
-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.py118
-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.py151
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/__init__.py0
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/expected_chunks1.json1
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/expected_chunks2.json8
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/expected_chunks3.json1
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/expected_chunks4.json2
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/expected_chunks5.json2
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/expected_chunks6.json2
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/expected_locators1.json34
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/expected_locators2.json34
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/expected_locators3.json34
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/expected_locators4.json49
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/expected_locators5.json34
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/expected_locators6.json4
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/r1/setup.sh2
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/r1/sharpd.conf7
-rw-r--r--tests/topotests/srv6_locator_custom_bits_length/r1/zebra.conf22
-rwxr-xr-xtests/topotests/srv6_locator_custom_bits_length/test_srv6_locator.py147
-rw-r--r--tests/topotests/srv6_locator_usid/__init__.py0
-rw-r--r--tests/topotests/srv6_locator_usid/expected_chunks_1.json1
-rw-r--r--tests/topotests/srv6_locator_usid/expected_chunks_2.json8
-rw-r--r--tests/topotests/srv6_locator_usid/expected_chunks_3.json1
-rw-r--r--tests/topotests/srv6_locator_usid/expected_chunks_4.json1
-rw-r--r--tests/topotests/srv6_locator_usid/expected_chunks_5.json2
-rw-r--r--tests/topotests/srv6_locator_usid/expected_chunks_6.json2
-rw-r--r--tests/topotests/srv6_locator_usid/expected_chunks_7.json2
-rw-r--r--tests/topotests/srv6_locator_usid/expected_chunks_8.json2
-rw-r--r--tests/topotests/srv6_locator_usid/expected_locators_1.json20
-rw-r--r--tests/topotests/srv6_locator_usid/expected_locators_2.json20
-rw-r--r--tests/topotests/srv6_locator_usid/expected_locators_3.json20
-rw-r--r--tests/topotests/srv6_locator_usid/expected_locators_4.json35
-rw-r--r--tests/topotests/srv6_locator_usid/expected_locators_5.json36
-rw-r--r--tests/topotests/srv6_locator_usid/expected_locators_6.json35
-rw-r--r--tests/topotests/srv6_locator_usid/expected_locators_7.json19
-rw-r--r--tests/topotests/srv6_locator_usid/expected_locators_8.json4
-rw-r--r--tests/topotests/srv6_locator_usid/r1/setup.sh2
-rw-r--r--tests/topotests/srv6_locator_usid/r1/sharpd.conf7
-rw-r--r--tests/topotests/srv6_locator_usid/r1/zebra.conf20
-rwxr-xr-xtests/topotests/srv6_locator_usid/test_srv6_locator_usid.py255
-rw-r--r--tests/topotests/srv6_static_route/__init__.py0
-rw-r--r--tests/topotests/srv6_static_route/expected_srv6_route.json49
-rw-r--r--tests/topotests/srv6_static_route/r1/mgmtd.conf0
-rw-r--r--tests/topotests/srv6_static_route/r1/setup.sh2
-rw-r--r--tests/topotests/srv6_static_route/r1/staticd.conf9
-rw-r--r--tests/topotests/srv6_static_route/r1/zebra.conf10
-rwxr-xr-xtests/topotests/srv6_static_route/test_srv6_route.py90
-rw-r--r--tests/topotests/static_routing_mpls/r1/zebra.conf16
-rw-r--r--tests/topotests/static_routing_mpls/r2/zebra.conf18
-rw-r--r--tests/topotests/static_routing_mpls/test_static_routing_mpls.py139
-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.py1321
-rw-r--r--tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py1731
-rw-r--r--tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py1346
-rw-r--r--tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py976
-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.py1119
-rw-r--r--tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py2006
-rw-r--r--tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py876
-rw-r--r--tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py972
-rw-r--r--tests/topotests/static_simple/r1/mgmtd.conf1
-rw-r--r--tests/topotests/static_simple/r1/staticd.conf1
-rw-r--r--tests/topotests/static_simple/r1/zebra.conf11
-rw-r--r--tests/topotests/static_simple/test_static_simple.py214
-rw-r--r--tests/topotests/subdir.am7
-rwxr-xr-xtests/topotests/tc_basic/test_tc_basic.py120
-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.py149
-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.py110
-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.py153
-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.py117
-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_1_vrf_before.json27
-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.py372
-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.py95
-rw-r--r--tests/topotests/zebra_seg6local_route/r1/routes.json123
-rw-r--r--tests/topotests/zebra_seg6local_route/r1/setup.sh6
-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.py110
-rw-r--r--tests/zebra/subdir.am17
-rw-r--r--tests/zebra/test_lm_plugin.c121
-rw-r--r--tests/zebra/test_lm_plugin.py5
-rw-r--r--tests/zebra/test_lm_plugin.refout2
4938 files changed, 579124 insertions, 0 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..681438f
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1,67 @@
+*.log
+*.sum
+*.xml
+frr-northbound.proto
+frr_northbound*
+.pytest_cache
+/bgpd/test_aspath
+/bgpd/test_bgp_table
+/bgpd/test_capability
+/bgpd/test_ecommunity
+/bgpd/test_mp_attr
+/bgpd/test_mpath
+/bgpd/test_packet
+/bgpd/test_peer_attr
+/isisd/test_fuzz_isis_tlv
+/isisd/test_fuzz_isis_tlv_tests.h
+/isisd/test_isis_lspdb
+/isisd/test_isis_spf
+/isisd/test_isis_vertex_queue
+/lib/cli/test_cli
+/lib/cli/test_cli_clippy.c
+/lib/cli/test_commands
+/lib/cli/test_commands_defun.c
+/lib/northbound/test_oper_data
+/lib/cxxcompat
+/lib/fuzz_zlog
+/lib/test_assert
+/lib/test_atomlist
+/lib/test_buffer
+/lib/test_checksum
+/lib/test_frrscript
+/lib/test_darr
+/lib/test_frrlua
+/lib/test_graph
+/lib/test_grpc
+/lib/test_heavy
+/lib/test_heavy_thread
+/lib/test_heavy_wq
+/lib/test_idalloc
+/lib/test_memory
+/lib/test_nexthop
+/lib/test_nexthop_iter
+/lib/test_ntop
+/lib/test_plist
+/lib/test_prefix2str
+/lib/test_printfrr
+/lib/test_privs
+/lib/test_resolver
+/lib/test_ringbuf
+/lib/test_segv
+/lib/test_seqlock
+/lib/test_sig
+/lib/test_skiplist
+/lib/test_srcdest_table
+/lib/test_stream
+/lib/test_table
+/lib/test_timer_correctness
+/lib/test_timer_performance
+/lib/test_ttable
+/lib/test_typelist
+/lib/test_versioncmp
+/lib/test_xref
+/lib/test_zlog
+/lib/test_zmq
+/ospf6d/test_lsdb
+/ospf6d/test_lsdb_clippy.c
+/zebra/test_lm_plugin
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..dd4594f
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C .. check
+%: ALWAYS
+ @$(MAKE) -s -C .. tests/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/tests/bgpd/subdir.am b/tests/bgpd/subdir.am
new file mode 100644
index 0000000..5148e7e
--- /dev/null
+++ b/tests/bgpd/subdir.am
@@ -0,0 +1,82 @@
+if !BGPD
+PYTEST_IGNORE += --ignore=bgpd/
+endif
+BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) $(UST_LIBS) -lm
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_aspath
+endif
+tests_bgpd_test_aspath_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_aspath_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_aspath_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_aspath_SOURCES = tests/bgpd/test_aspath.c
+EXTRA_DIST += tests/bgpd/test_aspath.py
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_bgp_table
+endif
+tests_bgpd_test_bgp_table_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_bgp_table_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_bgp_table_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_bgp_table_SOURCES = tests/bgpd/test_bgp_table.c
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_capability
+endif
+tests_bgpd_test_capability_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_capability_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_capability_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_capability_SOURCES = tests/bgpd/test_capability.c
+EXTRA_DIST += tests/bgpd/test_capability.py
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_ecommunity
+endif
+tests_bgpd_test_ecommunity_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_ecommunity_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_ecommunity_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_ecommunity_SOURCES = tests/bgpd/test_ecommunity.c
+EXTRA_DIST += tests/bgpd/test_ecommunity.py
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_mp_attr
+endif
+tests_bgpd_test_mp_attr_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_mp_attr_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_mp_attr_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_mp_attr_SOURCES = tests/bgpd/test_mp_attr.c
+EXTRA_DIST += tests/bgpd/test_mp_attr.py
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_mpath
+endif
+tests_bgpd_test_mpath_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_mpath_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_mpath_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_mpath_SOURCES = tests/bgpd/test_mpath.c
+EXTRA_DIST += tests/bgpd/test_mpath.py
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_packet
+endif
+tests_bgpd_test_packet_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_packet_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_packet_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_packet_SOURCES = tests/bgpd/test_packet.c
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_peer_attr
+endif
+tests_bgpd_test_peer_attr_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_peer_attr_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_peer_attr_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_peer_attr_SOURCES = tests/bgpd/test_peer_attr.c
+EXTRA_DIST += tests/bgpd/test_peer_attr.py
diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c
new file mode 100644
index 0000000..799733b
--- /dev/null
+++ b/tests/bgpd/test_aspath.c
@@ -0,0 +1,1463 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2005 Sun Microsystems, Inc.
+ */
+
+#include <zebra.h>
+
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "queue.h"
+#include "filter.h"
+#include "frr_pthread.h"
+
+#include "bgpd/bgpd.c"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_packet.h"
+
+#define VT100_RESET "\x1b[0m"
+#define VT100_RED "\x1b[31m"
+#define VT100_GREEN "\x1b[32m"
+#define VT100_YELLOW "\x1b[33m"
+#define OK VT100_GREEN "OK" VT100_RESET
+#define FAILED VT100_RED "failed" VT100_RESET
+
+/* need these to link in libbgp */
+struct zebra_privs_t bgpd_privs = {};
+struct event_loop *master = NULL;
+
+static int failed = 0;
+
+/* specification for a test - what the results should be */
+struct test_spec {
+ const char *shouldbe; /* the string the path should parse to */
+ const char *shouldbe_delete_confed; /* ditto, but once confeds are
+ deleted */
+ const unsigned int hops; /* aspath_count_hops result */
+ const unsigned int confeds; /* aspath_count_confeds */
+ const int private_as; /* whether the private_as check should pass or
+ fail */
+#define NOT_ALL_PRIVATE 0
+#define ALL_PRIVATE 1
+ const as_t does_loop; /* an ASN which should trigger loop-check */
+ const as_t doesnt_loop; /* one which should not */
+ const as_t first; /* the first ASN, if there is one */
+#define NULL_ASN 0
+};
+
+
+/* test segments to parse and validate, and use for other tests */
+static struct test_segment {
+ const char *name;
+ const char *desc;
+ const uint8_t asdata[1024];
+ int len;
+ struct test_spec sp;
+ enum asnotation_mode asnotation;
+} test_segments[] = {
+ {
+ /* 0 */
+ "seq1",
+ "seq(8466,3,52737,4096)",
+ {0x2, 0x4, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00},
+ 10,
+ {"8466 3 52737 4096", "8466 3 52737 4096", 4, 0,
+ NOT_ALL_PRIVATE, 4096, 4, 8466},
+ 0,
+ },
+ {
+ /* 1 */
+ "seq2",
+ "seq(8722) seq(4)",
+ {0x2, 0x1, 0x22, 0x12, 0x2, 0x1, 0x00, 0x04},
+ 8,
+ {
+ "8722 4",
+ "8722 4",
+ 2,
+ 0,
+ NOT_ALL_PRIVATE,
+ 4,
+ 5,
+ 8722,
+ },
+ 0,
+ },
+ {
+ /* 2 */
+ "seq3",
+ "seq(8466,3,52737,4096,8722,4)",
+ {0x2, 0x6, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x22,
+ 0x12, 0x00, 0x04},
+ 14,
+ {"8466 3 52737 4096 8722 4", "8466 3 52737 4096 8722 4", 6, 0,
+ NOT_ALL_PRIVATE, 3, 5, 8466},
+ 0,
+ },
+ {
+ /* 3 */
+ "seqset",
+ "seq(8482,51457) set(5204)",
+ {0x2, 0x2, 0x21, 0x22, 0xc9, 0x01, 0x1, 0x1, 0x14, 0x54},
+ 10,
+ {"8482 51457 {5204}", "8482 51457 {5204}", 3, 0,
+ NOT_ALL_PRIVATE, 5204, 51456, 8482},
+ 0,
+ },
+ {
+ /* 4 */
+ "seqset2",
+ "seq(8467, 59649) set(4196,48658) set(17322,30745)",
+ {0x2, 0x2, 0x21, 0x13, 0xe9, 0x01, 0x1, 0x2, 0x10, 0x64, 0xbe,
+ 0x12, 0x1, 0x2, 0x43, 0xaa, 0x78, 0x19},
+ 18,
+ {"8467 59649 {4196,48658} {17322,30745}",
+ "8467 59649 {4196,48658} {17322,30745}", 4, 0, NOT_ALL_PRIVATE,
+ 48658, 1, 8467},
+ 0,
+ },
+ {
+ /* 5 */
+ "multi",
+ "seq(6435,59408,21665) set(2457,61697,4369), seq(1842,41590,51793)",
+ {0x2, 0x3, 0x19, 0x23, 0xe8, 0x10, 0x54, 0xa1,
+ 0x1, 0x3, 0x09, 0x99, 0xf1, 0x01, 0x11, 0x11,
+ 0x2, 0x3, 0x07, 0x32, 0xa2, 0x76, 0xca, 0x51},
+ 24,
+ {"6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
+ "6435 59408 21665 {2457,4369,61697} 1842 41590 51793", 7, 0,
+ NOT_ALL_PRIVATE, 51793, 1, 6435},
+ 0,
+ },
+ {
+ /* 6 */
+ "confed",
+ "confseq(123,456,789)",
+ {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15},
+ 8,
+ {"(123 456 789)", "", 0, 3, NOT_ALL_PRIVATE, 789, 1, NULL_ASN},
+ 0,
+ },
+ {
+ /* 7 */
+ "confed2",
+ "confseq(123,456,789) confseq(111,222)",
+ {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15, 0x3, 0x2, 0x00,
+ 0x6f, 0x00, 0xde},
+ 14,
+ {"(123 456 789) (111 222)", "", 0, 5, NOT_ALL_PRIVATE, 111, 1,
+ NULL_ASN},
+ 0,
+ },
+ {
+ /* 8 */
+ "confset",
+ "confset(456,123,789)",
+ {0x4, 0x3, 0x01, 0xc8, 0x00, 0x7b, 0x03, 0x15},
+ 8,
+ {"[123,456,789]", "", 0, 1, NOT_ALL_PRIVATE, 123, 1, NULL_ASN},
+ 0,
+ },
+ {
+ /* 9 */
+ "confmulti",
+ "confseq(123,456,789) confset(222,111) seq(8722) set(4196,48658)",
+ {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15,
+ 0x4, 0x2, 0x00, 0xde, 0x00, 0x6f, 0x2, 0x1,
+ 0x22, 0x12, 0x1, 0x2, 0x10, 0x64, 0xbe, 0x12},
+ 24,
+ {"(123 456 789) [111,222] 8722 {4196,48658}",
+ "8722 {4196,48658}", 2, 4, NOT_ALL_PRIVATE, 123, 1, NULL_ASN},
+ 0,
+ },
+ {
+ /* 10 */
+ "seq4",
+ "seq(8466,2,52737,4096,8722,4)",
+ {0x2, 0x6, 0x21, 0x12, 0x00, 0x02, 0xce, 0x01, 0x10, 0x00, 0x22,
+ 0x12, 0x00, 0x04},
+ 14,
+ {"8466 2 52737 4096 8722 4", "8466 2 52737 4096 8722 4", 6, 0,
+ NOT_ALL_PRIVATE, 4096, 1, 8466},
+ 0,
+ },
+ {
+ /* 11 */
+ "tripleseq1",
+ "seq(8466,2,52737) seq(4096,8722,4) seq(8722)",
+ {0x2, 0x3, 0x21, 0x12, 0x00, 0x02, 0xce, 0x01, 0x2, 0x3,
+ 0x10, 0x00, 0x22, 0x12, 0x00, 0x04, 0x2, 0x1, 0x22, 0x12},
+ 20,
+ {"8466 2 52737 4096 8722 4 8722",
+ "8466 2 52737 4096 8722 4 8722", 7, 0, NOT_ALL_PRIVATE, 4096,
+ 1, 8466},
+ 0,
+ },
+ {
+ /* 12 */
+ "someprivate",
+ "seq(8466,64512,52737,65535)",
+ {0x2, 0x4, 0x21, 0x12, 0xfc, 0x00, 0xce, 0x01, 0xff, 0xff},
+ 10,
+ {"8466 64512 52737 65535", "8466 64512 52737 65535", 4, 0,
+ NOT_ALL_PRIVATE, 65535, 4, 8466},
+ 0,
+ },
+ {
+ /* 13 */
+ "allprivate",
+ "seq(65534,64512,64513,65535)",
+ {0x2, 0x4, 0xff, 0xfe, 0xfc, 0x00, 0xfc, 0x01, 0xff, 0xff},
+ 10,
+ {"65534 64512 64513 65535", "65534 64512 64513 65535", 4, 0,
+ ALL_PRIVATE, 65534, 4, 65534},
+ 0,
+ },
+ {
+ /* 14 */
+ "long",
+ "seq(8466,3,52737,4096,34285,<repeated 49 more times>)",
+ {
+ 0x2, 0xfa, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10,
+ 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce,
+ 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03,
+ 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00,
+ 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12,
+ 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21,
+ 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed,
+ 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85,
+ 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00,
+ 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10,
+ 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce,
+ 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03,
+ 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00,
+ 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12,
+ 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21,
+ 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed,
+ 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85,
+ 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00,
+ 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10,
+ 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce,
+ 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03,
+ 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00,
+ 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12,
+ 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21,
+ 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed,
+ 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85,
+ 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00,
+ 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10,
+ 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce,
+ 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03,
+ 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00,
+ 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12,
+ 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21,
+ 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed,
+ 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85,
+ 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00,
+ 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10,
+ 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce,
+ 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03,
+ 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00,
+ 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12,
+ 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21,
+ 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed,
+ 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85,
+ 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00,
+ 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10,
+ 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce,
+ 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03,
+ 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00,
+ 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed,
+ },
+ 502,
+ {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285",
+
+ "8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285",
+ 250, 0, NOT_ALL_PRIVATE, 4096, 4, 8466},
+ 0,
+ },
+ {
+ /* 15 */
+ "seq1extra",
+ "seq(8466,3,52737,4096,3456)",
+ {0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x0d,
+ 0x80},
+ 12,
+ {"8466 3 52737 4096 3456", "8466 3 52737 4096 3456", 5, 0,
+ NOT_ALL_PRIVATE, 4096, 4, 8466},
+ 0,
+ },
+ {
+ /* 16 */
+ "empty",
+ "<empty>",
+ {},
+ 0,
+ {"", "", 0, 0, 0, 0, 0, 0},
+ 0,
+ },
+ {
+ /* 17 */
+ "redundantset",
+ "seq(8466,3,52737,4096,3456) set(7099,8153,8153,8153)",
+ {0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x0d, 0x80, 0x1, 0x4, 0x1b, 0xbb,
+ 0x1f, 0xd9, 0x1f, 0xd9, 0x1f, 0xd9},
+ 22,
+ {/* We shouldn't ever /generate/ such paths. However, we should
+ * cope with them fine.
+ */
+ "8466 3 52737 4096 3456 {7099,8153}",
+ "8466 3 52737 4096 3456 {7099,8153}", 6, 0, NOT_ALL_PRIVATE,
+ 4096, 4, 8466},
+ 0,
+ },
+ {
+ /* 18 */
+ "reconcile_lead_asp",
+ "seq(6435,59408,21665) set(23456,23456,23456), seq(23456,23456,23456)",
+ {0x2, 0x3, 0x19, 0x23, 0xe8, 0x10, 0x54, 0xa1,
+ 0x1, 0x3, 0x5b, 0xa0, 0x5b, 0xa0, 0x5b, 0xa0,
+ 0x2, 0x3, 0x5b, 0xa0, 0x5b, 0xa0, 0x5b, 0xa0},
+ 24,
+ {"6435 59408 21665 {23456} 23456 23456 23456",
+ "6435 59408 21665 {23456} 23456 23456 23456", 7, 0,
+ NOT_ALL_PRIVATE, 23456, 1, 6435},
+ 0,
+ },
+ {
+ /* 19 */
+ "reconcile_new_asp",
+ "set(2457,61697,4369), seq(1842,41591,51793)",
+ {0x1, 0x3, 0x09, 0x99, 0xf1, 0x01, 0x11, 0x11, 0x2, 0x3, 0x07,
+ 0x32, 0xa2, 0x77, 0xca, 0x51},
+ 16,
+ {"{2457,4369,61697} 1842 41591 51793",
+ "{2457,4369,61697} 1842 41591 51793", 4, 0, NOT_ALL_PRIVATE,
+ 51793, 1, 2457},
+ 0,
+ },
+ {
+ /* 20 */
+ "reconcile_confed",
+ "confseq(123,456,789) confset(456,124,788) seq(6435,59408,21665) set(23456,23456,23456), seq(23456,23456,23456)",
+ {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15, 0x4, 0x3,
+ 0x01, 0xc8, 0x00, 0x7c, 0x03, 0x14, 0x2, 0x3, 0x19, 0x23,
+ 0xe8, 0x10, 0x54, 0xa1, 0x1, 0x3, 0x5b, 0xa0, 0x5b, 0xa0,
+ 0x5b, 0xa0, 0x2, 0x3, 0x5b, 0xa0, 0x5b, 0xa0, 0x5b, 0xa0},
+ 40,
+ {"(123 456 789) [124,456,788] 6435 59408 21665 {23456} 23456 23456 23456",
+ "6435 59408 21665 {23456} 23456 23456 23456", 7, 4,
+ NOT_ALL_PRIVATE, 23456, 1, 6435},
+ 0,
+ },
+ {
+ /* 21 */
+ "reconcile_start_trans",
+ "seq(23456,23456,23456) seq(6435,59408,21665)",
+ {
+ 0x2,
+ 0x3,
+ 0x5b,
+ 0xa0,
+ 0x5b,
+ 0xa0,
+ 0x5b,
+ 0xa0,
+ 0x2,
+ 0x3,
+ 0x19,
+ 0x23,
+ 0xe8,
+ 0x10,
+ 0x54,
+ 0xa1,
+ },
+ 16,
+ {"23456 23456 23456 6435 59408 21665",
+ "23456 23456 23456 6435 59408 21665", 6, 0, NOT_ALL_PRIVATE,
+ 21665, 1, 23456},
+ 0,
+ },
+ {
+ /* 22 */
+ "reconcile_start_trans4",
+ "seq(1842,41591,51793) seq(6435,59408,21665)",
+ {
+ 0x2,
+ 0x3,
+ 0x07,
+ 0x32,
+ 0xa2,
+ 0x77,
+ 0xca,
+ 0x51,
+ 0x2,
+ 0x3,
+ 0x19,
+ 0x23,
+ 0xe8,
+ 0x10,
+ 0x54,
+ 0xa1,
+ },
+ 16,
+ {"1842 41591 51793 6435 59408 21665",
+ "1842 41591 51793 6435 59408 21665", 6, 0, NOT_ALL_PRIVATE,
+ 41591, 1, 1842},
+ 0,
+ },
+ {
+ /* 23 */
+ "reconcile_start_trans_error",
+ "seq(23456,23456,23456) seq(6435,59408)",
+ {
+ 0x2,
+ 0x3,
+ 0x5b,
+ 0xa0,
+ 0x5b,
+ 0xa0,
+ 0x5b,
+ 0xa0,
+ 0x2,
+ 0x2,
+ 0x19,
+ 0x23,
+ 0xe8,
+ 0x10,
+ },
+ 14,
+ {"23456 23456 23456 6435 59408", "23456 23456 23456 6435 59408",
+ 5, 0, NOT_ALL_PRIVATE, 59408, 1, 23456},
+ 0,
+ },
+ {
+ /* 24 */
+ "redundantset2",
+ "seq(8466,3,52737,4096,3456) set(7099,8153,8153,8153,7099)",
+ {
+ 0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x0d, 0x80, 0x1, 0x5, 0x1b, 0xbb,
+ 0x1f, 0xd9, 0x1f, 0xd9, 0x1f, 0xd9, 0x1b, 0xbb,
+ },
+ 24,
+ {/* We should weed out duplicate set members. */
+ "8466 3 52737 4096 3456 {7099,8153}",
+ "8466 3 52737 4096 3456 {7099,8153}", 6, 0, NOT_ALL_PRIVATE,
+ 4096, 4, 8466},
+ 0,
+ },
+ {
+ /* 25 */
+ "zero-size overflow",
+ "#ASNs = 0, data = seq(8466 3 52737 4096 3456)",
+ {0x2, 0x0, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x0d,
+ 0x80},
+ 12,
+ {NULL, NULL, 0, 0, 0, 0, 0, 0},
+ 0,
+ },
+ {
+ /* 26 */
+ "zero-size overflow + valid segment",
+ "seq(#AS=0:8466 3 52737),seq(4096 3456)",
+ {0x2, 0x0, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x2, 0x2, 0x10,
+ 0x00, 0x0d, 0x80},
+ 14,
+ {NULL, NULL, 0, 0, 0, 0, 0, 0},
+ 0,
+ },
+ {
+ /* 27 */
+ "invalid segment type",
+ "type=8(4096 3456)",
+ {0x8, 0x2, 0x10, 0x00, 0x0d, 0x80},
+ 14,
+ {NULL, NULL, 0, 0, 0, 0, 0, 0},
+ 0,
+ },
+ {
+ /* 28 */
+ "BGP_AS_ZERO",
+ "seq(8466,3,52737,0,4096)",
+ {0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x00, 0x00, 0x10,
+ 0x00},
+ 12,
+ {"8466 3 52737 0 4096", "8466 3 52737 0 4096", 5, 0,
+ NOT_ALL_PRIVATE, 4096, 4, 8466},
+ 0,
+ },
+ {
+ /* 29 */
+ "seq3_asdot+",
+ "seq(0.8466,0.3,0.52737,0.4096,0.8722,0.4)",
+ {0x2, 0x6, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x22,
+ 0x12, 0x00, 0x04},
+ 14,
+ {"0.8466 0.3 0.52737 0.4096 0.8722 0.4",
+ "0.8466 0.3 0.52737 0.4096 0.8722 0.4", 6, 0, NOT_ALL_PRIVATE,
+ 3, 5, 8466},
+ ASNOTATION_DOTPLUS,
+ },
+ {
+ /* 30 */
+ "confmulti_asdot+",
+ "confseq(0.123,0.456,0.789) confset(0.222,0.111) seq(0.8722) set(0.4196,0.48658)",
+ {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15,
+ 0x4, 0x2, 0x00, 0xde, 0x00, 0x6f, 0x2, 0x1,
+ 0x22, 0x12, 0x1, 0x2, 0x10, 0x64, 0xbe, 0x12},
+ 24,
+ {"(0.123 0.456 0.789) [0.111,0.222] 0.8722 {0.4196,0.48658}",
+ "0.8722 {0.4196,0.48658}", 2, 4, NOT_ALL_PRIVATE, 123, 1,
+ NULL_ASN},
+ ASNOTATION_DOTPLUS,
+ },
+ {
+ /* 31 */
+ "someprivate asdot+",
+ "seq(0.8466,0.64512,0.52737,0.65535)",
+ {0x2, 0x4, 0x21, 0x12, 0xfc, 0x00, 0xce, 0x01, 0xff, 0xff},
+ 10,
+ {"0.8466 0.64512 0.52737 0.65535",
+ "0.8466 0.64512 0.52737 0.65535", 4, 0, NOT_ALL_PRIVATE, 65535,
+ 4, 8466},
+ ASNOTATION_DOTPLUS,
+ },
+ {
+ /* 32 */
+ "BGP_AS_ZERO asdot+",
+ "seq(0.8466,0.3,0.52737,0.0,0.4096)",
+ {0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x00, 0x00, 0x10,
+ 0x00},
+ 12,
+ {"0.8466 0.3 0.52737 0.0 0.4096",
+ "0.8466 0.3 0.52737 0.0 0.4096", 5, 0, NOT_ALL_PRIVATE, 4096,
+ 4, 8466},
+ ASNOTATION_DOTPLUS,
+ },
+ {NULL, NULL, {0}, 0, {NULL, 0, 0}}};
+
+#define COMMON_ATTRS \
+ BGP_ATTR_FLAG_TRANS, BGP_ATTR_ORIGIN, 1, BGP_ORIGIN_EGP, \
+ BGP_ATTR_FLAG_TRANS, BGP_ATTR_NEXT_HOP, 4, 192, 0, 2, 0
+#define COMMON_ATTR_SIZE 11
+
+/* */
+static struct aspath_tests {
+ const char *desc;
+ const struct test_segment *segment;
+ const char *shouldbe; /* String it should evaluate to */
+ const enum as4 {
+ AS4_DATA,
+ AS2_DATA
+ } as4; /* whether data should be as4 or not (ie as2) */
+ const int result; /* expected result for bgp_attr_parse */
+ const int cap; /* capabilities to set for peer */
+ const char attrheader[1024];
+ size_t len;
+ const struct test_segment *old_segment;
+} aspath_tests[] = {
+ /* 0 */
+ {
+ "basic test",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS2_DATA,
+ 0,
+ 0,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 10,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 1 */
+ {
+ "length too short",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS2_DATA,
+ -1,
+ 0,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 8,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 2 */
+ {
+ "length too long",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS2_DATA,
+ -1,
+ 0,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 12,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 3 */
+ {
+ "incorrect flag",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS2_DATA,
+ -1,
+ 0,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS_PATH,
+ 10,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 4 */
+ {
+ "as4_path, with as2 format data",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS2_DATA,
+ -1,
+ 0,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS4_PATH,
+ 10,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 5 */
+ {
+ "as4, with incorrect attr length",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS4_DATA,
+ -1,
+ PEER_CAP_AS4_RCV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS4_PATH,
+ 10,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 6 */
+ {
+ "basic 4-byte as-path",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS4_DATA,
+ 0,
+ PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 18,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 7 */
+ {
+ "4b AS_PATH: too short",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS4_DATA,
+ -1,
+ PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 16,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 8 */
+ {
+ "4b AS_PATH: too long",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS4_DATA,
+ -1,
+ PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 20,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 9 */
+ {
+ "4b AS_PATH: too long2",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS4_DATA,
+ -1,
+ PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 22,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 10 */
+ {
+ "4b AS_PATH: bad flags",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS4_DATA,
+ -1,
+ PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS_PATH,
+ 18,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 11 */
+ {
+ "4b AS4_PATH w/o AS_PATH",
+ &test_segments[6],
+ NULL,
+ AS4_DATA,
+ -2,
+ PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS4_PATH,
+ 14,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 12 */
+ {
+ "4b AS4_PATH: confed",
+ &test_segments[6],
+ "8466 3 52737 4096 (123 456 789)",
+ AS4_DATA,
+ 0,
+ PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS4_PATH,
+ 14,
+ },
+ COMMON_ATTR_SIZE + 3,
+ &test_segments[0],
+ },
+ /* 13 */
+ {
+ "4b AS4_PATH: BGP_AS_ZERO",
+ &test_segments[28],
+ "8466 3 52737 0 4096",
+ AS4_DATA,
+ -2,
+ PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS4_PATH,
+ 22,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ {NULL, NULL, NULL, 0, 0, 0, {0}, 0},
+};
+
+/* prepending tests */
+static struct tests {
+ const struct test_segment *test1;
+ const struct test_segment *test2;
+ struct test_spec sp;
+} prepend_tests[] = {
+ /* 0 */
+ {
+ &test_segments[0],
+ &test_segments[1],
+ {"8466 3 52737 4096 8722 4", "8466 3 52737 4096 8722 4", 6, 0,
+ NOT_ALL_PRIVATE, 4096, 1, 8466},
+ },
+ /* 1 */
+ {&test_segments[1],
+ &test_segments[3],
+ {"8722 4 8482 51457 {5204}", "8722 4 8482 51457 {5204}", 5, 0,
+ NOT_ALL_PRIVATE, 5204, 1, 8722}},
+ /* 2 */
+ {
+ &test_segments[3],
+ &test_segments[4],
+ {"8482 51457 {5204} 8467 59649 {4196,48658} {17322,30745}",
+ "8482 51457 {5204} 8467 59649 {4196,48658} {17322,30745}", 7,
+ 0, NOT_ALL_PRIVATE, 5204, 1, 8482},
+ },
+ /* 3 */
+ {&test_segments[4],
+ &test_segments[5],
+ {"8467 59649 {4196,48658} {17322,30745} 6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
+ "8467 59649 {4196,48658} {17322,30745} 6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
+ 11, 0, NOT_ALL_PRIVATE, 61697, 1, 8467}},
+ /* 4 */
+ {
+ &test_segments[5],
+ &test_segments[6],
+ {"6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
+ "6435 59408 21665 {2457,4369,61697} 1842 41590 51793", 7, 0,
+ NOT_ALL_PRIVATE, 1842, 1, 6435},
+ },
+ /* 5 */
+ {&test_segments[6],
+ &test_segments[7],
+ {"(123 456 789) (123 456 789) (111 222)", "", 0, 8, NOT_ALL_PRIVATE,
+ 111, 1, 0}},
+ {&test_segments[7],
+ &test_segments[8],
+ {"(123 456 789) (111 222) [123,456,789]", "", 0, 6, NOT_ALL_PRIVATE,
+ 111, 1, 0}},
+ {
+ &test_segments[8],
+ &test_segments[9],
+ {"[123,456,789] (123 456 789) [111,222] 8722 {4196,48658}",
+ "8722 {4196,48658}", 2, 5, NOT_ALL_PRIVATE, 456, 1, NULL_ASN},
+ },
+ {
+ &test_segments[9],
+ &test_segments[8],
+ {"(123 456 789) [111,222] 8722 {4196,48658} [123,456,789]",
+ "8722 {4196,48658}", 2, 5, NOT_ALL_PRIVATE, 48658, 1,
+ NULL_ASN},
+ },
+ {
+ &test_segments[14],
+ &test_segments[11],
+ {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 2 52737 4096 8722 4 8722",
+
+ "8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 2 52737 4096 8722 4 8722",
+ 257, 0, NOT_ALL_PRIVATE, 4096, 1000, 8466},
+ },
+ {NULL,
+ NULL,
+ {
+ NULL, 0, 0, 0, 0, 0, 0,
+ }},
+};
+
+struct tests reconcile_tests[] = {
+ {
+ &test_segments[18],
+ &test_segments[19],
+ {"6435 59408 21665 {2457,4369,61697} 1842 41591 51793",
+ "6435 59408 21665 {2457,4369,61697} 1842 41591 51793", 7, 0,
+ NOT_ALL_PRIVATE, 51793, 1, 6435},
+ },
+ {
+ &test_segments[19],
+ &test_segments[18],
+ /* AS_PATH (19) has more hops than NEW_AS_PATH,
+ * so just AS_PATH should be used (though, this practice
+ * is bad imho).
+ */
+ {"{2457,4369,61697} 1842 41591 51793 6435 59408 21665 {23456} 23456 23456 23456",
+ "{2457,4369,61697} 1842 41591 51793 6435 59408 21665 {23456} 23456 23456 23456",
+ 11, 0, NOT_ALL_PRIVATE, 51793, 1, 6435},
+ },
+ {
+ &test_segments[20],
+ &test_segments[19],
+ {"(123 456 789) [124,456,788] 6435 59408 21665 {2457,4369,61697} 1842 41591 51793",
+ "6435 59408 21665 {2457,4369,61697} 1842 41591 51793", 7, 4,
+ NOT_ALL_PRIVATE, 51793, 1, 6435},
+ },
+ {
+ &test_segments[21],
+ &test_segments[22],
+ {"1842 41591 51793 6435 59408 21665",
+ "1842 41591 51793 6435 59408 21665", 6, 0, NOT_ALL_PRIVATE,
+ 51793, 1, 1842},
+ },
+ {
+ &test_segments[23],
+ &test_segments[22],
+ {"23456 23456 23456 6435 59408 1842 41591 51793 6435 59408 21665",
+ "23456 23456 23456 6435 59408 1842 41591 51793 6435 59408 21665",
+ 11, 0, NOT_ALL_PRIVATE, 51793, 1, 1842},
+ },
+ {NULL,
+ NULL,
+ {
+ NULL, 0, 0, 0, 0, 0, 0,
+ }},
+};
+
+struct tests aggregate_tests[] = {
+ {
+ &test_segments[0],
+ &test_segments[2],
+ {"8466 3 52737 4096 {4,8722}", "8466 3 52737 4096 {4,8722}", 5,
+ 0, NOT_ALL_PRIVATE, 4, 1, 8466},
+ },
+ {
+ &test_segments[2],
+ &test_segments[0],
+ {"8466 3 52737 4096 {4,8722}", "8466 3 52737 4096 {4,8722}", 5,
+ 0, NOT_ALL_PRIVATE, 8722, 1, 8466},
+ },
+ {
+ &test_segments[2],
+ &test_segments[10],
+ {"8466 {2,3,4,4096,8722,52737}", "8466 {2,3,4,4096,8722,52737}",
+ 2, 0, NOT_ALL_PRIVATE, 8722, 5, 8466},
+ },
+ {
+ &test_segments[10],
+ &test_segments[2],
+ {"8466 {2,3,4,4096,8722,52737}", "8466 {2,3,4,4096,8722,52737}",
+ 2, 0, NOT_ALL_PRIVATE, 2, 20000, 8466},
+ },
+
+ {
+ &test_segments[5],
+ &test_segments[18],
+ {"6435 59408 21665 {1842,2457,4369,23456,41590,51793,61697}",
+ "6435 59408 21665 {1842,2457,4369,23456,41590,51793,61697}", 4,
+ 0, NOT_ALL_PRIVATE, 41590, 1, 6435},
+ },
+
+ {NULL, NULL, {NULL, 0, 0}},
+};
+
+struct compare_tests {
+ int test_index1;
+ int test_index2;
+#define CMP_RES_YES 1
+#define CMP_RES_NO 0
+ char shouldbe_cmp;
+ char shouldbe_confed;
+} left_compare[] = {
+ {0, 1, CMP_RES_NO, CMP_RES_NO}, {0, 2, CMP_RES_YES, CMP_RES_NO},
+ {0, 11, CMP_RES_YES, CMP_RES_NO}, {0, 15, CMP_RES_YES, CMP_RES_NO},
+ {0, 16, CMP_RES_NO, CMP_RES_NO}, {1, 11, CMP_RES_NO, CMP_RES_NO},
+ {6, 7, CMP_RES_NO, CMP_RES_YES}, {6, 8, CMP_RES_NO, CMP_RES_NO},
+ {7, 8, CMP_RES_NO, CMP_RES_NO}, {1, 9, CMP_RES_YES, CMP_RES_NO},
+ {0, 9, CMP_RES_NO, CMP_RES_NO}, {3, 9, CMP_RES_NO, CMP_RES_NO},
+ {0, 6, CMP_RES_NO, CMP_RES_NO}, {1, 6, CMP_RES_NO, CMP_RES_NO},
+ {0, 8, CMP_RES_NO, CMP_RES_NO}, {1, 8, CMP_RES_NO, CMP_RES_NO},
+ {11, 6, CMP_RES_NO, CMP_RES_NO}, {11, 7, CMP_RES_NO, CMP_RES_NO},
+ {11, 8, CMP_RES_NO, CMP_RES_NO}, {9, 6, CMP_RES_NO, CMP_RES_YES},
+ {9, 7, CMP_RES_NO, CMP_RES_YES}, {9, 8, CMP_RES_NO, CMP_RES_NO},
+};
+
+/* make an aspath from a data stream */
+static struct aspath *make_aspath(const uint8_t *data, size_t len, int use32bit,
+ enum asnotation_mode asnotation)
+{
+ struct stream *s = NULL;
+ struct aspath *as;
+ if (len) {
+ s = stream_new(len);
+ stream_put(s, data, len);
+ }
+ as = aspath_parse(s, len, use32bit, asnotation);
+
+ if (s)
+ stream_free(s);
+
+ return as;
+}
+
+static void printbytes(const uint8_t *bytes, int len)
+{
+ int i = 0;
+ while (i < len) {
+ if (i % 2)
+ printf("%02hhx%s", bytes[i], " ");
+ else
+ printf("0x%02hhx", bytes[i]);
+ i++;
+ }
+ printf("\n");
+}
+
+/* validate the given aspath */
+static int validate(struct aspath *as, const struct test_spec *sp)
+{
+ size_t bytes, bytes4;
+ int fails = 0;
+ const uint8_t *out;
+ static struct stream *s;
+ struct aspath *asinout, *asconfeddel, *asstr, *as4;
+
+ if (as == NULL && sp->shouldbe == NULL) {
+ printf("Correctly failed to parse\n");
+ return fails;
+ }
+
+ out = aspath_snmp_pathseg(as, &bytes);
+ asinout = make_aspath(out, bytes, 0, as->asnotation);
+ /* Excercise AS4 parsing a bit, with a dogfood test */
+ if (!s)
+ s = stream_new(BGP_MAX_PACKET_SIZE);
+ bytes4 = aspath_put(s, as, 1);
+ as4 = make_aspath(STREAM_DATA(s), bytes4, 1, as->asnotation);
+
+ asn_relax_as_zero(true);
+ asstr = aspath_str2aspath(sp->shouldbe, as->asnotation);
+ asn_relax_as_zero(false);
+
+ asconfeddel = aspath_delete_confed_seq(aspath_dup(asinout));
+
+ printf("got: %s\n", aspath_print(as));
+
+ /* the parsed path should match the specified 'shouldbe' string.
+ * We should pass the "eat our own dog food" test, be able to output
+ * this path and then input it again. Ie the path resulting from:
+ *
+ * aspath_parse(aspath_put(as))
+ *
+ * should:
+ *
+ * - also match the specified 'shouldbe' value
+ * - hash to same value as original path
+ * - have same hops and confed counts as original, and as the
+ * the specified counts
+ *
+ * aspath_str2aspath() and shouldbe should match
+ *
+ * We do the same for:
+ *
+ * aspath_parse(aspath_put(as,USE32BIT))
+ *
+ * Confederation related tests:
+ * - aspath_delete_confed_seq(aspath) should match shouldbe_confed
+ * - aspath_delete_confed_seq should be idempotent.
+ */
+ if (strcmp(aspath_print(as), sp->shouldbe)
+ /* hash validation */
+ || (aspath_key_make(as) != aspath_key_make(asinout))
+ /* by string */
+ || strcmp(aspath_print(asinout), sp->shouldbe)
+ /* By 4-byte parsing */
+ || strcmp(aspath_print(as4), sp->shouldbe)
+ /* by various path counts */
+ || (aspath_count_hops(as) != sp->hops)
+ || (aspath_count_confeds(as) != sp->confeds)
+ || (aspath_count_hops(asinout) != sp->hops)
+ || (aspath_count_confeds(asinout) != sp->confeds)) {
+ failed++;
+ fails++;
+ printf("shouldbe:\n%s\n", sp->shouldbe);
+ printf("as4:\n%s\n", aspath_print(as4));
+ printf("hash keys: in: %d out->in: %d\n", aspath_key_make(as),
+ aspath_key_make(asinout));
+ printf("hops: %d, counted %d %d\n", sp->hops,
+ aspath_count_hops(as), aspath_count_hops(asinout));
+ printf("confeds: %d, counted %d %d\n", sp->confeds,
+ aspath_count_confeds(as), aspath_count_confeds(asinout));
+ printf("out->in:\n%s\nbytes: ", aspath_print(asinout));
+ printbytes(out, bytes);
+ }
+ /* basic confed related tests */
+ if ((aspath_print(asconfeddel) == NULL
+ && sp->shouldbe_delete_confed != NULL)
+ || (aspath_print(asconfeddel) != NULL
+ && sp->shouldbe_delete_confed == NULL)
+ || strcmp(aspath_print(asconfeddel), sp->shouldbe_delete_confed)
+ /* delete_confed_seq should be idempotent */
+ || (aspath_key_make(asconfeddel)
+ != aspath_key_make(aspath_delete_confed_seq(asconfeddel)))) {
+ failed++;
+ fails++;
+ printf("as-path minus confeds is: %s\n",
+ aspath_print(asconfeddel));
+ printf("as-path minus confeds should be: %s\n",
+ sp->shouldbe_delete_confed);
+ }
+ /* aspath_str2aspath test */
+ if ((aspath_print(asstr) == NULL && sp->shouldbe != NULL)
+ || (aspath_print(asstr) != NULL && sp->shouldbe == NULL)
+ || strcmp(aspath_print(asstr), sp->shouldbe)) {
+ failed++;
+ fails++;
+ printf("asstr: %s\n", aspath_print(asstr));
+ }
+
+ /* loop, private and first as checks */
+ if ((sp->does_loop && aspath_loop_check(as, sp->does_loop) == 0)
+ || (sp->doesnt_loop && aspath_loop_check(as, sp->doesnt_loop) != 0)
+ || (aspath_private_as_check(as) != sp->private_as)
+ || (aspath_firstas_check(as, sp->first) && sp->first == 0)) {
+ failed++;
+ fails++;
+ printf("firstas: %d, got %d\n", sp->first,
+ aspath_firstas_check(as, sp->first));
+ printf("loop does: %d %d, doesn't: %d %d\n", sp->does_loop,
+ aspath_loop_check(as, sp->does_loop), sp->doesnt_loop,
+ aspath_loop_check(as, sp->doesnt_loop));
+ printf("private check: %d %d\n", sp->private_as,
+ aspath_private_as_check(as));
+ }
+ aspath_unintern(&asinout);
+ aspath_unintern(&as4);
+
+ aspath_free(asconfeddel);
+ aspath_free(asstr);
+ stream_reset(s);
+
+ return fails;
+}
+
+static void empty_get_test(void)
+{
+ struct aspath *as = aspath_empty_get();
+ struct test_spec sp = {"", "", 0, 0, 0, 0, 0, 0};
+
+ printf("empty_get_test, as: %s\n", aspath_print(as));
+ if (!validate(as, &sp))
+ printf("%s\n", OK);
+ else
+ printf("%s!\n", FAILED);
+
+ printf("\n");
+
+ aspath_free(as);
+}
+
+/* basic parsing test */
+static void parse_test(struct test_segment *t)
+{
+ struct aspath *asp;
+
+ printf("%s: %s\n", t->name, t->desc);
+
+ asp = make_aspath(t->asdata, t->len, 0, t->asnotation);
+
+ printf("aspath: %s\nvalidating...:\n", aspath_print(asp));
+
+ if (!validate(asp, &t->sp))
+ printf(OK "\n");
+ else
+ printf(FAILED "\n");
+
+ printf("\n");
+
+ aspath_unintern(&asp);
+}
+
+/* prepend testing */
+static void prepend_test(struct tests *t)
+{
+ struct aspath *asp1, *asp2, *ascratch;
+
+ printf("prepend %s: %s\n", t->test1->name, t->test1->desc);
+ printf("to %s: %s\n", t->test2->name, t->test2->desc);
+
+ asp1 = make_aspath(t->test1->asdata, t->test1->len, 0,
+ ASNOTATION_PLAIN);
+ asp2 = make_aspath(t->test2->asdata, t->test2->len, 0,
+ ASNOTATION_PLAIN);
+
+ ascratch = aspath_dup(asp2);
+ aspath_unintern(&asp2);
+
+ asp2 = aspath_prepend(asp1, ascratch);
+
+ printf("aspath: %s\n", aspath_print(asp2));
+
+ if (!validate(asp2, &t->sp))
+ printf("%s\n", OK);
+ else
+ printf("%s!\n", FAILED);
+
+ printf("\n");
+ aspath_unintern(&asp1);
+ aspath_free(asp2);
+}
+
+/* empty-prepend testing */
+static void empty_prepend_test(struct test_segment *t)
+{
+ struct aspath *asp1, *asp2, *ascratch;
+
+ printf("empty prepend %s: %s\n", t->name, t->desc);
+
+ asp1 = make_aspath(t->asdata, t->len, 0, t->asnotation);
+ asp2 = aspath_empty(t->asnotation);
+
+ ascratch = aspath_dup(asp2);
+ aspath_unintern(&asp2);
+
+ asp2 = aspath_prepend(asp1, ascratch);
+
+ printf("aspath: %s\n", aspath_print(asp2));
+
+ if (!validate(asp2, &t->sp))
+ printf(OK "\n");
+ else
+ printf(FAILED "!\n");
+
+ printf("\n");
+ aspath_unintern(&asp1);
+ aspath_free(asp2);
+}
+
+/* as2+as4 reconciliation testing */
+static void as4_reconcile_test(struct tests *t)
+{
+ struct aspath *asp1, *asp2, *ascratch;
+
+ printf("reconciling %s:\n %s\n", t->test1->name, t->test1->desc);
+ printf("with %s:\n %s\n", t->test2->name, t->test2->desc);
+
+ asp1 = make_aspath(t->test1->asdata, t->test1->len, 0,
+ ASNOTATION_PLAIN);
+ asp2 = make_aspath(t->test2->asdata, t->test2->len, 0,
+ ASNOTATION_PLAIN);
+
+ ascratch = aspath_reconcile_as4(asp1, asp2);
+
+ if (!validate(ascratch, &t->sp))
+ printf(OK "\n");
+ else
+ printf(FAILED "!\n");
+
+ printf("\n");
+ aspath_unintern(&asp1);
+ aspath_unintern(&asp2);
+ aspath_free(ascratch);
+}
+
+
+/* aggregation testing */
+static void aggregate_test(struct tests *t)
+{
+ struct aspath *asp1, *asp2, *ascratch;
+
+ printf("aggregate %s: %s\n", t->test1->name, t->test1->desc);
+ printf("with %s: %s\n", t->test2->name, t->test2->desc);
+
+ asp1 = make_aspath(t->test1->asdata, t->test1->len, 0,
+ ASNOTATION_PLAIN);
+ asp2 = make_aspath(t->test2->asdata, t->test2->len, 0,
+ ASNOTATION_PLAIN);
+
+ ascratch = aspath_aggregate(asp1, asp2);
+
+ if (!validate(ascratch, &t->sp))
+ printf(OK "\n");
+ else
+ printf(FAILED "!\n");
+
+ printf("\n");
+ aspath_unintern(&asp1);
+ aspath_unintern(&asp2);
+ aspath_free(ascratch);
+ /* aspath_unintern (ascratch);*/
+}
+
+/* cmp_left tests */
+static void cmp_test(void)
+{
+ unsigned int i;
+#define CMP_TESTS_MAX (sizeof(left_compare) / sizeof(struct compare_tests))
+
+ for (i = 0; i < CMP_TESTS_MAX; i++) {
+ struct test_segment *t1 =
+ &test_segments[left_compare[i].test_index1];
+ struct test_segment *t2 =
+ &test_segments[left_compare[i].test_index2];
+ struct aspath *asp1, *asp2;
+
+ printf("left cmp %s: %s\n", t1->name, t1->desc);
+ printf("and %s: %s\n", t2->name, t2->desc);
+
+ asp1 = make_aspath(t1->asdata, t1->len, 0, ASNOTATION_PLAIN);
+ asp2 = make_aspath(t2->asdata, t2->len, 0, ASNOTATION_PLAIN);
+
+ if (aspath_cmp_left(asp1, asp2) != left_compare[i].shouldbe_cmp
+ || aspath_cmp_left(asp2, asp1)
+ != left_compare[i].shouldbe_cmp
+ || aspath_cmp_left_confed(asp1, asp2)
+ != left_compare[i].shouldbe_confed
+ || aspath_cmp_left_confed(asp2, asp1)
+ != left_compare[i].shouldbe_confed) {
+ failed++;
+ printf(FAILED "\n");
+ printf("result should be: cmp: %d, confed: %d\n",
+ left_compare[i].shouldbe_cmp,
+ left_compare[i].shouldbe_confed);
+ printf("got: cmp %d, cmp_confed: %d\n",
+ aspath_cmp_left(asp1, asp2),
+ aspath_cmp_left_confed(asp1, asp2));
+ printf("path1: %s\npath2: %s\n", aspath_print(asp1),
+ aspath_print(asp2));
+ } else
+ printf(OK "\n");
+
+ printf("\n");
+ aspath_unintern(&asp1);
+ aspath_unintern(&asp2);
+ }
+}
+
+static int handle_attr_test(struct aspath_tests *t)
+{
+ struct bgp bgp = {0};
+ struct peer peer = {0};
+ struct attr attr = {0};
+ int ret;
+ int initfail = failed;
+ struct aspath *asp;
+ size_t datalen;
+
+ asp = make_aspath(t->segment->asdata, t->segment->len, 0,
+ t->segment->asnotation);
+ bgp.asnotation = t->segment->asnotation;
+
+ peer.curr = stream_new(BGP_MAX_PACKET_SIZE);
+ peer.connection = bgp_peer_connection_new(&peer);
+ peer.connection->obuf = stream_fifo_new();
+ peer.bgp = &bgp;
+ peer.host = (char *)"none";
+ peer.connection->fd = -1;
+ peer.cap = t->cap;
+ peer.max_packet_size = BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE;
+
+ stream_write(peer.curr, t->attrheader, t->len);
+ datalen = aspath_put(peer.curr, asp, t->as4 == AS4_DATA);
+ if (t->old_segment) {
+ char dummyaspath[] = {BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH,
+ t->old_segment->len};
+ stream_write(peer.curr, dummyaspath, sizeof(dummyaspath));
+ stream_write(peer.curr, t->old_segment->asdata,
+ t->old_segment->len);
+ datalen += sizeof(dummyaspath) + t->old_segment->len;
+ }
+
+ ret = bgp_attr_parse(&peer, &attr, t->len + datalen, NULL, NULL);
+
+ if (ret != t->result) {
+ printf("bgp_attr_parse returned %d, expected %d\n", ret,
+ t->result);
+ printf("datalen %zd\n", datalen);
+ failed++;
+ }
+ if (ret != 0)
+ goto out;
+
+ if (t->shouldbe && attr.aspath == NULL) {
+ printf("aspath is NULL, but should be: %s\n", t->shouldbe);
+ failed++;
+ }
+ if (t->shouldbe && attr.aspath
+ && strcmp(attr.aspath->str, t->shouldbe)) {
+ printf("attr str and 'shouldbe' mismatched!\n"
+ "attr str: %s\n"
+ "shouldbe: %s\n",
+ attr.aspath->str, t->shouldbe);
+ failed++;
+ }
+ if (!t->shouldbe && attr.aspath) {
+ printf("aspath should be NULL, but is: %s\n", attr.aspath->str);
+ failed++;
+ }
+
+out:
+ aspath_unintern(&attr.aspath);
+ aspath_unintern(&asp);
+ return failed - initfail;
+}
+
+static void attr_test(struct aspath_tests *t)
+{
+ printf("%s\n", t->desc);
+ printf("%s\n\n", handle_attr_test(t) ? FAILED : OK);
+}
+
+int main(void)
+{
+ int i = 0;
+ qobj_init();
+ bgp_master_init(event_master_create(NULL), BGP_SOCKET_SNDBUF_SIZE,
+ list_new());
+ master = bm->master;
+ bgp_option_set(BGP_OPT_NO_LISTEN);
+ bgp_attr_init();
+
+ while (test_segments[i].name) {
+ printf("test %u\n", i);
+ parse_test(&test_segments[i]);
+ empty_prepend_test(&test_segments[i++]);
+ }
+ i = 0;
+
+ while (prepend_tests[i].test1) {
+ printf("prepend test %u\n", i);
+ prepend_test(&prepend_tests[i++]);
+ }
+
+ i = 0;
+ while (aggregate_tests[i].test1) {
+ printf("aggregate test %u\n", i);
+ aggregate_test(&aggregate_tests[i++]);
+ }
+
+ i = 0;
+
+ while (reconcile_tests[i].test1) {
+ printf("reconcile test %u\n", i);
+ as4_reconcile_test(&reconcile_tests[i++]);
+ }
+
+ i = 0;
+
+ cmp_test();
+
+ i = 0;
+
+ empty_get_test();
+
+ i = 0;
+
+ frr_pthread_init();
+ bgp_pthreads_init();
+ bgp_pth_ka->running = true;
+
+ while (aspath_tests[i].desc) {
+ printf("aspath_attr test %d\n", i);
+ attr_test(&aspath_tests[i++]);
+ }
+
+ printf("failures: %d\n", failed);
+ printf("aspath count: %ld\n", aspath_count());
+
+ return (failed + aspath_count());
+}
diff --git a/tests/bgpd/test_aspath.py b/tests/bgpd/test_aspath.py
new file mode 100644
index 0000000..88579ad
--- /dev/null
+++ b/tests/bgpd/test_aspath.py
@@ -0,0 +1,84 @@
+import frrtest
+import re
+
+re_okfail = re.compile(
+ r"^(?:\x1b\[3[12]m)?(?P<ret>OK|failed)".encode("utf8"), re.MULTILINE
+)
+
+
+class TestAspath(frrtest.TestMultiOut):
+ program = "./test_aspath"
+
+ def _parsertest(self, line):
+ if not hasattr(self, "parserno"):
+ self.parserno = -1
+ self.parserno += 1
+
+ self._onesimple("test %d" % self.parserno)
+ self._okfail("%s:" % line, okfail=re_okfail)
+ self._okfail("empty prepend %s:" % line, okfail=re_okfail)
+
+ def _attrtest(self, line):
+ if not hasattr(self, "attrno"):
+ self.attrno = -1
+ self.attrno += 1
+
+ self._onesimple("aspath_attr test %d" % self.attrno)
+ self._okfail(line, okfail=re_okfail)
+
+
+TestAspath.parsertest("seq1")
+TestAspath.parsertest("seq2")
+TestAspath.parsertest("seq3")
+TestAspath.parsertest("seqset")
+TestAspath.parsertest("seqset2")
+TestAspath.parsertest("multi")
+TestAspath.parsertest("confed")
+TestAspath.parsertest("confed2")
+TestAspath.parsertest("confset")
+TestAspath.parsertest("confmulti")
+TestAspath.parsertest("seq4")
+TestAspath.parsertest("tripleseq1")
+TestAspath.parsertest("someprivate")
+TestAspath.parsertest("allprivate")
+TestAspath.parsertest("long")
+TestAspath.parsertest("seq1extra")
+TestAspath.parsertest("empty")
+TestAspath.parsertest("redundantset")
+TestAspath.parsertest("reconcile_lead_asp")
+TestAspath.parsertest("reconcile_new_asp")
+TestAspath.parsertest("reconcile_confed")
+TestAspath.parsertest("reconcile_start_trans")
+TestAspath.parsertest("reconcile_start_trans4")
+TestAspath.parsertest("reconcile_start_trans_error")
+TestAspath.parsertest("redundantset2")
+TestAspath.parsertest("zero-size overflow")
+TestAspath.parsertest("zero-size overflow + valid segment")
+TestAspath.parsertest("invalid segment type")
+TestAspath.parsertest("BGP_AS_ZERO")
+
+for i in range(10):
+ TestAspath.okfail("prepend test %d" % i)
+for i in range(5):
+ TestAspath.okfail("aggregate test %d" % i)
+for i in range(5):
+ TestAspath.okfail("reconcile test %d" % i)
+for _ in range(22):
+ TestAspath.okfail("left cmp ")
+
+TestAspath.okfail("empty_get_test")
+
+TestAspath.attrtest("basic test")
+TestAspath.attrtest("length too short")
+TestAspath.attrtest("length too long")
+TestAspath.attrtest("incorrect flag")
+TestAspath.attrtest("as4_path, with as2 format data")
+TestAspath.attrtest("as4, with incorrect attr length")
+TestAspath.attrtest("basic 4-byte as-path")
+TestAspath.attrtest("4b AS_PATH: too short")
+TestAspath.attrtest("4b AS_PATH: too long")
+TestAspath.attrtest("4b AS_PATH: too long2")
+TestAspath.attrtest("4b AS_PATH: bad flags")
+TestAspath.attrtest("4b AS4_PATH w/o AS_PATH")
+TestAspath.attrtest("4b AS4_PATH: confed")
+TestAspath.attrtest("4b AS4_PATH: BGP_AS_ZERO")
diff --git a/tests/bgpd/test_bgp_table.c b/tests/bgpd/test_bgp_table.c
new file mode 100644
index 0000000..bed86f5
--- /dev/null
+++ b/tests/bgpd/test_bgp_table.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BGP Routing table range lookup test
+ * Copyright (C) 2012 OSR.
+ * Copyright (C) 2018 Marcel Röthke (marcel.roethke@haw-hamburg.de), for HAW
+ * Hamburg
+ *
+ * This file is part of FRRouting
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "table.h"
+#include "bgpd/bgp_table.h"
+#include "linklist.h"
+
+/* Satisfy link requirements from including bgpd.h */
+struct zebra_privs_t bgpd_privs = {0};
+/*
+ * test_node_t
+ *
+ * Information that is kept for each node in the radix tree.
+ */
+struct test_node_t {
+
+ /*
+ * Human readable representation of the string. Allocated using
+ * malloc()/dup().
+ */
+ char *prefix_str;
+};
+
+/*
+ * add_node
+ *
+ * Add the given prefix (passed in as a string) to the given table.
+ */
+static void add_node(struct bgp_table *table, const char *prefix_str)
+{
+ struct prefix_ipv4 p;
+ struct test_node_t *node;
+ struct bgp_dest *dest;
+
+ assert(prefix_str);
+
+ if (str2prefix_ipv4(prefix_str, &p) <= 0)
+ assert(0);
+
+ dest = bgp_node_get(table, (struct prefix *)&p);
+ if (dest->info) {
+ assert(0);
+ return;
+ }
+
+ node = malloc(sizeof(struct test_node_t));
+ assert(node);
+ node->prefix_str = strdup(prefix_str);
+ assert(node->prefix_str);
+ dest->info = node;
+}
+
+static bool prefix_in_array(const struct prefix *p, struct prefix *prefix_array,
+ size_t prefix_array_size)
+{
+ for (size_t i = 0; i < prefix_array_size; ++i) {
+ if (prefix_same(p, &prefix_array[i]))
+ return true;
+ }
+ return false;
+}
+
+static void check_lookup_result(struct bgp_dest *match, va_list arglist)
+{
+ char *prefix_str;
+ struct prefix *prefixes = NULL;
+ size_t prefix_count = 0;
+
+ while ((prefix_str = va_arg(arglist, char *))) {
+ ++prefix_count;
+ prefixes = realloc(prefixes, sizeof(*prefixes) * prefix_count);
+
+ if (str2prefix(prefix_str, &prefixes[prefix_count - 1]) <= 0)
+ assert(0);
+ }
+
+ /* check if the result is empty and if it is allowd to be empty */
+ assert((prefix_count == 0 && !match) || prefix_count > 0);
+ if (!match)
+ return;
+
+ struct bgp_dest *dest = match;
+
+ while ((dest = bgp_route_next_until(dest, match))) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ if (bgp_dest_has_bgp_path_info_data(dest)
+ && !prefix_in_array(dest_p, prefixes, prefix_count)) {
+ printf("prefix %pFX was not expected!\n", dest_p);
+ assert(0);
+ }
+ }
+}
+
+static void do_test(struct bgp_table *table, const char *prefix, ...)
+{
+ va_list arglist;
+ struct prefix p;
+
+
+ va_start(arglist, prefix);
+ printf("\nDoing lookup for %s\n", prefix);
+ if (str2prefix(prefix, &p) <= 0)
+ assert(0);
+ struct bgp_dest *dest = bgp_table_subtree_lookup(table, &p);
+
+ check_lookup_result(dest, arglist);
+
+ va_end(arglist);
+
+ printf("Checks successfull\n");
+}
+
+/*
+ * test_range_lookup
+ */
+static void test_range_lookup(void)
+{
+ struct bgp_table *table = bgp_table_init(NULL, AFI_IP, SAFI_UNICAST);
+
+ printf("Testing bgp_table_range_lookup\n");
+
+ printf("Setup bgp_table");
+ const char *prefixes[] = {"1.16.0.0/16", "1.16.128.0/18",
+ "1.16.192.0/18", "1.16.64.0/19",
+ "1.16.160.0/19", "1.16.32.0/20",
+ "1.16.32.0/21", "16.0.0.0/16"};
+
+ int num_prefixes = array_size(prefixes);
+
+ for (int i = 0; i < num_prefixes; i++)
+ add_node(table, prefixes[i]);
+
+ do_test(table, "1.16.0.0/17", "1.16.64.0/19", "1.16.32.0/20",
+ "1.16.32.0/20", "1.16.32.0/21", NULL);
+ do_test(table, "1.16.128.0/17", "1.16.128.0/18", "1.16.192.0/18",
+ "1.16.160.0/19", NULL);
+
+ do_test(table, "1.16.0.0/16", "1.16.0.0/16", "1.16.128.0/18",
+ "1.16.192.0/18", "1.16.64.0/19", "1.16.160.0/19",
+ "1.16.32.0/20", "1.16.32.0/21", NULL);
+
+ do_test(table, "1.17.0.0/16", NULL);
+
+ do_test(table, "128.0.0.0/8", NULL);
+
+ do_test(table, "16.0.0.0/8", "16.0.0.0/16", NULL);
+
+ do_test(table, "0.0.0.0/2", "1.16.0.0/16", "1.16.128.0/18",
+ "1.16.192.0/18", "1.16.64.0/19", "1.16.160.0/19",
+ "1.16.32.0/20", "1.16.32.0/21", "16.0.0.0/16", NULL);
+}
+
+int main(void)
+{
+ test_range_lookup();
+}
diff --git a/tests/bgpd/test_bgp_table.py b/tests/bgpd/test_bgp_table.py
new file mode 100644
index 0000000..8f05442
--- /dev/null
+++ b/tests/bgpd/test_bgp_table.py
@@ -0,0 +1,9 @@
+import frrtest
+
+
+class TestTable(frrtest.TestMultiOut):
+ program = "./test_bgp_table"
+
+
+for i in range(7):
+ TestTable.onesimple("Checks successfull")
diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c
new file mode 100644
index 0000000..9d3d0ec
--- /dev/null
+++ b/tests/bgpd/test_capability.c
@@ -0,0 +1,984 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2007 Sun Microsystems, Inc.
+ */
+
+#include <zebra.h>
+
+#include "qobj.h"
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.h"
+#include "frr_pthread.h"
+
+#include "bgpd/bgpd.c"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_packet.h"
+
+#define VT100_RESET "\x1b[0m"
+#define VT100_RED "\x1b[31m"
+#define VT100_GREEN "\x1b[32m"
+#define VT100_YELLOW "\x1b[33m"
+
+#define CAPABILITY 0
+#define DYNCAP 1
+#define OPT_PARAM 2
+
+/* need these to link in libbgp */
+struct zebra_privs_t bgpd_privs = {};
+struct event_loop *master = NULL;
+
+static int failed = 0;
+static int tty = 0;
+
+/* test segments to parse and validate, and use for other tests */
+static struct test_segment {
+ const char *name;
+ const char *desc;
+ const uint8_t data[1024];
+ int len;
+#define SHOULD_PARSE 0
+#define SHOULD_ERR -1
+ int parses; /* whether it should parse or not */
+ as_t peek_for; /* what peek_for_as4_capability should say */
+
+ /* AFI/SAFI validation */
+ int validate_afi;
+ iana_afi_t afi;
+ iana_safi_t safi;
+#define VALID_AFI 1
+#define INVALID_AFI 0
+ int afi_valid;
+} test_segments[] = {
+ /* 0 */
+ {
+ "caphdr",
+ "capability header, and no more",
+ {CAPABILITY_CODE_REFRESH, 0x0},
+ 2,
+ SHOULD_PARSE,
+ },
+ /* 1 */
+ {
+ "nodata",
+ "header, no data but length says there is",
+ {0x1, 0xa},
+ 2,
+ SHOULD_ERR,
+ },
+ /* 2 */
+ {
+ "padded",
+ "valid, with padding",
+ {CAPABILITY_CODE_REFRESH, 0x2, 0x0, 0x0},
+ 4,
+ SHOULD_PARSE,
+ },
+ /* 3 */
+ {
+ "minsize",
+ "violates minsize requirement",
+ {CAPABILITY_CODE_ORF, 0x2, 0x0, 0x0},
+ 4,
+ SHOULD_ERR,
+ },
+ {NULL, NULL, {0}, 0, 0},
+};
+
+static struct test_segment mp_segments[] = {
+ {
+ "MP4",
+ "MP IP/Uni",
+ {0x1, 0x4, 0x0, 0x1, 0x0, 0x1},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ IANA_AFI_IPV4,
+ IANA_SAFI_UNICAST,
+ VALID_AFI,
+ },
+ {
+ "MPv6",
+ "MP IPv6/Uni",
+ {0x1, 0x4, 0x0, 0x2, 0x0, 0x1},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ IANA_AFI_IPV6,
+ IANA_SAFI_UNICAST,
+ VALID_AFI,
+ },
+ /* 5 */
+ {
+ "MP2",
+ "MP IP/Multicast",
+ {CAPABILITY_CODE_MP, 0x4, 0x0, 0x1, 0x0, 0x2},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ IANA_AFI_IPV4,
+ IANA_SAFI_MULTICAST,
+ VALID_AFI,
+ },
+ /* 6 */
+ {
+ "MP3",
+ "MP IP6/MPLS-labeled VPN",
+ {CAPABILITY_CODE_MP, 0x4, 0x0, 0x2, 0x0, 0x80},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ IANA_AFI_IPV6,
+ IANA_SAFI_MPLS_VPN,
+ VALID_AFI,
+ },
+ /* 7 */
+ {
+ "MP5",
+ "MP IP6/MPLS-VPN",
+ {CAPABILITY_CODE_MP, 0x4, 0x0, 0x2, 0x0, 0x4},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ IANA_AFI_IPV6,
+ IANA_SAFI_MPLS_VPN,
+ VALID_AFI,
+ },
+ /* 8 */
+ {
+ "MP6",
+ "MP IP4/MPLS-labeled VPN",
+ {CAPABILITY_CODE_MP, 0x4, 0x0, 0x1, 0x0, 0x80},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ IANA_AFI_IPV4,
+ IANA_SAFI_MPLS_VPN,
+ VALID_AFI,
+ },
+ /* 10 */
+ {
+ "MP8",
+ "MP unknown AFI/SAFI",
+ {CAPABILITY_CODE_MP, 0x4, 0x0, 0xa, 0x0, 0x81},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ 0xa,
+ 0x81,
+ INVALID_AFI, /* parses, but unknown */
+ },
+ /* 11 */
+ {
+ "MP-short",
+ "MP IP4/Unicast, length too short (< minimum)",
+ {CAPABILITY_CODE_MP, 0x2, 0x0, 0x1, 0x0, 0x1},
+ 6,
+ SHOULD_ERR,
+ },
+ /* 12 */
+ {
+ "MP-overflow",
+ "MP IP4/Unicast, length too long",
+ {CAPABILITY_CODE_MP, 0x6, 0x0, 0x1, 0x0, 0x1},
+ 6,
+ SHOULD_ERR,
+ 0,
+ 1,
+ IANA_AFI_IPV4,
+ IANA_SAFI_UNICAST,
+ VALID_AFI,
+ },
+ {NULL, NULL, {0}, 0, 0}};
+
+static struct test_segment misc_segments[] =
+ {
+ /* 13 */
+ {
+ "ORF",
+ "ORF, simple, single entry, single tuple",
+ {/* hdr */ CAPABILITY_CODE_ORF, 0x7,
+ /* mpc */ 0x0, 0x1, 0x0, 0x1,
+ /* num */ 0x1,
+ /* tuples */ 0x40, 0x3},
+ 9,
+ SHOULD_PARSE,
+ },
+ /* 14 */
+ {
+ "ORF-many",
+ "ORF, multi entry/tuple",
+ {
+ /* hdr */ CAPABILITY_CODE_ORF,
+ 0x21,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ ORF_MODE_BOTH,
+ 0x80,
+ ORF_MODE_RECEIVE,
+ 0x80,
+ ORF_MODE_SEND,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ ORF_MODE_BOTH,
+ 0x80,
+ ORF_MODE_RECEIVE,
+ 0x80,
+ ORF_MODE_SEND,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x2,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ ORF_MODE_RECEIVE,
+ 0x80,
+ ORF_MODE_SEND,
+ 0x80,
+ ORF_MODE_BOTH,
+ },
+ 35,
+ SHOULD_PARSE,
+ },
+ /* 15 */
+ {
+ "ORFlo",
+ "ORF, multi entry/tuple, hdr length too short",
+ {
+ /* hdr */ CAPABILITY_CODE_ORF,
+ 0x15,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x2,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ },
+ 35,
+ SHOULD_ERR, /* It should error on invalid
+ Route-Refresh.. */
+ },
+ /* 16 */
+ {"ORFlu",
+ "ORF, multi entry/tuple, length too long",
+ {
+ /* hdr */ 0x3,
+ 0x22,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x2,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ },
+ 35,
+ SHOULD_ERR},
+ /* 17 */
+ {
+ "ORFnu",
+ "ORF, multi entry/tuple, entry number too long",
+ {
+ /* hdr */ 0x3,
+ 0x21,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x1,
+ /* num */ 0x4,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x2,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ },
+ 35,
+ SHOULD_PARSE, /* parses, but last few tuples should be
+ gibberish */
+ },
+ /* 18 */
+ {
+ "ORFno",
+ "ORF, multi entry/tuple, entry number too short",
+ {
+ /* hdr */ 0x3,
+ 0x21,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x1,
+ /* num */ 0x1,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x2,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ },
+ 35,
+ SHOULD_PARSE, /* Parses, but should get gibberish
+ afi/safis */
+ },
+ /* 17 */
+ {
+ "ORFpad",
+ "ORF, multi entry/tuple, padded to align",
+ {
+ /* hdr */ 0x3,
+ 0x22,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x2,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ 0x00,
+ },
+ 36,
+ SHOULD_PARSE,
+ },
+ /* 19 */
+ {
+ "AS4",
+ "AS4 capability",
+ {0x41, 0x4, 0xab, 0xcd, 0xef,
+ 0x12}, /* AS: 2882400018 */
+ 6,
+ SHOULD_PARSE,
+ 2882400018,
+ },
+ {
+ "AS4",
+ "AS4 capability: short",
+ {0x41, 0x4, 0xab, 0xcd, 0xef}, /* AS: 2882400018 */
+ 5,
+ SHOULD_ERR,
+ },
+ {
+ "AS4",
+ "AS4 capability: long",
+ {0x41, 0x4, 0xab, 0xcd, 0xef, 0x12, 0x12},
+ 7,
+ SHOULD_ERR,
+ 2882400018,
+ },
+ {
+ "GR",
+ "GR capability",
+ {
+ /* hdr */ CAPABILITY_CODE_RESTART, 0xe,
+ /* R-bit, time */ 0xf1, 0x12,
+ /* afi */ 0x0, 0x1,
+ /* safi */ 0x1,
+ /* flags */ 0xf,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x1,
+ /* flags */ 0x0,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x2,
+ /* flags */ 0x1,
+ },
+ 16,
+ SHOULD_PARSE,
+ },
+ {
+ "GR-short",
+ "GR capability, but header length too short",
+ {
+ /* hdr */ 0x40, 0xa,
+ /* R-bit, time */ 0xf1, 0x12,
+ /* afi */ 0x0, 0x1,
+ /* safi */ 0x1,
+ /* flags */ 0xf,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x1,
+ /* flags */ 0x0,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x2,
+ /* flags */ 0x1,
+ },
+ 15 /* array is 16 though */,
+ SHOULD_ERR,
+ },
+ {
+ "GR-long",
+ "GR capability, but header length too long",
+ {
+ /* hdr */ 0x40, 0xf,
+ /* R-bit, time */ 0xf1, 0x12,
+ /* afi */ 0x0, 0x1,
+ /* safi */ 0x1,
+ /* flags */ 0xf,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x1,
+ /* flags */ 0x0,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x2,
+ /* flags */ 0x01,
+ },
+ 16,
+ SHOULD_ERR,
+ },
+ {
+ "GR-trunc",
+ "GR capability, but truncated",
+ {
+ /* hdr */ 0x40, 0xf,
+ /* R-bit, time */ 0xf1, 0x12,
+ /* afi */ 0x0, 0x1,
+ /* safi */ 0x1,
+ /* flags */ 0xf,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x1,
+ /* flags */ 0x0,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x2,
+ /* flags */ 0x1,
+ },
+ 15,
+ SHOULD_ERR,
+ },
+ {
+ "GR-empty",
+ "GR capability, but empty.",
+ {
+ /* hdr */ 0x40, 0x0,
+ },
+ 2,
+ SHOULD_ERR,
+ },
+ {
+ "MP-empty",
+ "MP capability, but empty.",
+ {
+ /* hdr */ 0x1, 0x0,
+ },
+ 2,
+ SHOULD_ERR,
+ },
+ {
+ "ORF-empty",
+ "ORF capability, but empty.",
+ {
+ /* hdr */ 0x3, 0x0,
+ },
+ 2,
+ SHOULD_ERR,
+ },
+ {
+ "AS4-empty",
+ "AS4 capability, but empty.",
+ {
+ /* hdr */ 0x41, 0x0,
+ },
+ 2,
+ SHOULD_ERR,
+ },
+ {
+ "dyn-empty",
+ "Dynamic capability, but empty.",
+ {
+ /* hdr */ 0x42, 0x0,
+ },
+ 2,
+ SHOULD_PARSE,
+ },
+ {
+ "dyn-old",
+ "Dynamic capability (deprecated version)",
+ {CAPABILITY_CODE_DYNAMIC, 0x0},
+ 2,
+ SHOULD_PARSE,
+ },
+ {
+ "Role",
+ "Role capability",
+ {
+ /* hdr */ 0x9, 0x1,
+ 0x1,
+ },
+ 3,
+ SHOULD_PARSE,
+ },
+ {
+ "Role-long",
+ "Role capability, but too long",
+ {
+ /* hdr */ 0x9, 0x4,
+ 0x0, 0x0, 0x0, 0x1,
+ },
+ 6,
+ SHOULD_ERR,
+ },
+ {
+ "Role-empty",
+ "Role capability, but empty.",
+ {
+ /* hdr */ 0x9, 0x0,
+ },
+ 2,
+ SHOULD_ERR,
+ },
+ {NULL, NULL, {0}, 0, 0}};
+
+/* DYNAMIC message */
+struct test_segment dynamic_cap_msgs[] = {
+ {
+ "DynCap",
+ "Dynamic Capability Message, IP/Multicast",
+ {0x0, 0x1, 0x4, 0x0, 0x1, 0x0, 0x2},
+ 7,
+ SHOULD_PARSE, /* horrible alignment, just as with ORF */
+ },
+ {
+ "DynCapLong",
+ "Dynamic Capability Message, IP/Multicast, truncated",
+ {0x0, 0x1, 0x4, 0x0, 0x1, 0x0, 0x2},
+ 5,
+ SHOULD_ERR,
+ },
+ {
+ "DynCapPadded",
+ "Dynamic Capability Message, IP/Multicast, padded",
+ {0x0, 0x1, 0x4, 0x0, 0x1, 0x0, 0x2, 0x0},
+ 8,
+ SHOULD_ERR, /* No way to tell padding from data.. */
+ },
+ {
+ "DynCapMPCpadded",
+ "Dynamic Capability Message, IP/Multicast, cap data padded",
+ {0x0, 0x1, 0x5, 0x0, 0x1, 0x0, 0x2, 0x0},
+ 8,
+ SHOULD_PARSE, /* You can though add padding to the capability
+ data */
+ },
+ {
+ "DynCapMPCoverflow",
+ "Dynamic Capability Message, IP/Multicast, cap data != length",
+ {0x0, 0x1, 0x3, 0x0, 0x1, 0x0, 0x2, 0x0},
+ 8,
+ SHOULD_ERR,
+ },
+ {NULL, NULL, {0}, 0, 0}};
+
+/* Entire Optional-Parameters block */
+struct test_segment opt_params[] = {
+ {
+ "Cap-singlets",
+ "One capability per Optional-Param",
+ {
+ 0x02, 0x06, 0x01, 0x04,
+ 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */
+ 0x02, 0x06, 0x01, 0x04,
+ 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
+ 0x02, 0x02, 0x80, 0x00, /* RR (old) */
+ 0x02, 0x02, 0x02, 0x00, /* RR */
+ },
+ 24,
+ SHOULD_PARSE,
+ },
+ {
+ "Cap-series",
+ "Series of capability, one Optional-Param",
+ {
+ 0x02, 0x10, 0x01, 0x04, 0x00, 0x01, 0x00,
+ 0x01, /* MP IPv4/Uni */
+ 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
+ 0x80, 0x00, /* RR (old) */
+ 0x02, 0x00, /* RR */
+ },
+ 18,
+ SHOULD_PARSE,
+ },
+ {
+ "AS4more",
+ "AS4 capability after other caps (singlets)",
+ {
+ 0x02, 0x06, 0x01, 0x04,
+ 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */
+ 0x02, 0x06, 0x01, 0x04,
+ 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
+ 0x02, 0x02, 0x80, 0x00, /* RR (old) */
+ 0x02, 0x02, 0x02, 0x00, /* RR */
+ 0x02, 0x06, 0x41, 0x04,
+ 0x00, 0x03, 0x00, 0x06 /* AS4: 1996614 */
+ },
+ 32,
+ SHOULD_PARSE,
+ 196614,
+ },
+ {
+ "AS4series",
+ "AS4 capability, in series of capabilities",
+ {
+ 0x02, 0x16, 0x01, 0x04, 0x00, 0x01,
+ 0x00, 0x01, /* MP IPv4/Uni */
+ 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
+ 0x80, 0x00, /* RR (old) */
+ 0x02, 0x00, /* RR */
+ 0x41, 0x04, 0x00, 0x03, 0x00, 0x06 /* AS4: 1996614 */
+ },
+ 24,
+ SHOULD_PARSE,
+ 196614,
+ },
+ {
+ "AS4real",
+ "AS4 capability, in series of capabilities",
+ {
+ 0x02, 0x06, 0x01, 0x04,
+ 0x00, 0x01, 0x00, 0x01, /* MP IPv4/uni */
+ 0x02, 0x06, 0x01, 0x04,
+ 0x00, 0x02, 0x00, 0x01, /* MP IPv6/uni */
+ 0x02, 0x02, 0x80, 0x00, /* RR old */
+ 0x02, 0x02, 0x02, 0x00, /* RR */
+ 0x02, 0x06, 0x41, 0x04,
+ 0x00, 0x03, 0x00, 0x06, /* AS4 */
+ },
+ 32,
+ SHOULD_PARSE,
+ 196614,
+ },
+ {
+ "AS4real2",
+ "AS4 capability, in series of capabilities",
+ {
+ 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, 0x02,
+ 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, 0x02, 0x02,
+ 0x80, 0x00, 0x02, 0x02, 0x02, 0x00, 0x02, 0x06, 0x41,
+ 0x04, 0x00, 0x00, 0xfc, 0x03, 0x02, 0x09, 0x82, 0x07,
+ 0x00, 0x01, 0x00, 0x01, 0x01, 0x80, 0x03, 0x02, 0x09,
+ 0x03, 0x07, 0x00, 0x01, 0x00, 0x01, 0x01, 0x40, 0x03,
+ 0x02, 0x02, 0x42, 0x00,
+ },
+ 58,
+ SHOULD_PARSE,
+ 64515,
+ },
+
+ {NULL, NULL, {0}, 0, 0}};
+
+/* basic parsing test */
+static void parse_test(struct peer *peer, struct test_segment *t, int type)
+{
+ int ret;
+ int capability = 0;
+ as_t as4 = 0;
+ int oldfailed = failed;
+ int len = t->len;
+#define RANDOM_FUZZ 35
+
+ stream_reset(peer->curr);
+ stream_put(peer->curr, NULL, RANDOM_FUZZ);
+ stream_set_getp(peer->curr, RANDOM_FUZZ);
+
+ switch (type) {
+ case CAPABILITY:
+ stream_putc(peer->curr, BGP_OPEN_OPT_CAP);
+ stream_putc(peer->curr, t->len);
+ break;
+ case DYNCAP:
+ /* for (i = 0; i < BGP_MARKER_SIZE; i++)
+ stream_putc (peer->, 0xff);
+ stream_putw (s, 0);
+ stream_putc (s, BGP_MSG_CAPABILITY);*/
+ break;
+ }
+ stream_write(peer->curr, t->data, t->len);
+
+ printf("%s: %s\n", t->name, t->desc);
+
+ switch (type) {
+ case CAPABILITY:
+ len += 2; /* to cover the OPT-Param header */
+ _FALLTHROUGH
+ case OPT_PARAM:
+ printf("len: %u\n", len);
+ /* peek_for_as4 wants getp at capibility*/
+ as4 = peek_for_as4_capability(peer, len);
+ printf("peek_for_as4: as4 is %u\n", as4);
+ /* and it should leave getp as it found it */
+ assert(stream_get_getp(peer->curr) == RANDOM_FUZZ);
+
+ ret = bgp_open_option_parse(peer, len, &capability);
+ break;
+ case DYNCAP:
+ ret = bgp_capability_receive(peer->connection, peer, t->len);
+ break;
+ default:
+ printf("unknown type %u\n", type);
+ exit(1);
+ }
+
+ if (ret != BGP_Stop && t->validate_afi) {
+ afi_t afi;
+ safi_t safi;
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(t->afi, t->safi, &afi, &safi)) {
+ if (t->afi_valid == VALID_AFI)
+ failed++;
+ }
+ printf("MP: %u(%u)/%u(%u): recv %u, nego %u\n", t->afi, afi,
+ t->safi, safi, peer->afc_recv[afi][safi],
+ peer->afc_nego[afi][safi]);
+
+ if (t->afi_valid == VALID_AFI) {
+
+ if (!peer->afc_recv[afi][safi])
+ failed++;
+ if (!peer->afc_nego[afi][safi])
+ failed++;
+ }
+ }
+
+ if (as4 != t->peek_for) {
+ printf("as4 %u != %u\n", as4, t->peek_for);
+ failed++;
+ }
+
+ /*
+ * Some of the functions used return BGP_Stop on error and some return
+ * -1. If we have -1, keep it; if we have BGP_Stop, transform it to the
+ * correct pass/fail code
+ */
+ if (ret != -1)
+ ret = (ret == BGP_Stop) ? -1 : 0;
+
+ printf("parsed?: %s\n", ret ? "no" : "yes");
+
+ if (ret != t->parses) {
+ printf("t->parses: %d\nret: %d\n", t->parses, ret);
+ failed++;
+ }
+
+ if (tty)
+ printf("%s",
+ (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET
+ : VT100_GREEN "OK" VT100_RESET);
+ else
+ printf("%s", (failed > oldfailed) ? "failed!" : "OK");
+
+ if (failed)
+ printf(" (%u)", failed);
+
+ printf("\n\n");
+}
+
+static struct bgp *bgp;
+static as_t asn = 100;
+
+int main(void)
+{
+ struct peer *peer;
+ int i, j;
+
+ conf_bgp_debug_neighbor_events = -1UL;
+ conf_bgp_debug_packet = -1UL;
+ conf_bgp_debug_as4 = -1UL;
+ term_bgp_debug_neighbor_events = -1UL;
+ term_bgp_debug_packet = -1UL;
+ term_bgp_debug_as4 = -1UL;
+
+ qobj_init();
+ master = event_master_create(NULL);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
+ vrf_init(NULL, NULL, NULL, NULL);
+ bgp_option_set(BGP_OPT_NO_LISTEN);
+
+ frr_pthread_init();
+ bgp_pthreads_init();
+ bgp_pth_ka->running = true;
+
+ if (fileno(stdout) >= 0)
+ tty = isatty(fileno(stdout));
+
+ if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, NULL,
+ ASNOTATION_PLAIN) < 0)
+ return -1;
+
+ peer = peer_create_accept(bgp);
+ peer->host = (char *)"foo";
+
+ for (i = AFI_IP; i < AFI_MAX; i++)
+ for (j = SAFI_UNICAST; j < SAFI_MAX; j++) {
+ peer->afc[i][j] = 1;
+ peer->afc_adv[i][j] = 1;
+ }
+
+ peer->curr = stream_new(BGP_MAX_PACKET_SIZE);
+
+ i = 0;
+ while (mp_segments[i].name)
+ parse_test(peer, &mp_segments[i++], CAPABILITY);
+
+ /* These tests assume mp_segments tests set at least
+ * one of the afc_nego's
+ */
+ i = 0;
+ while (test_segments[i].name)
+ parse_test(peer, &test_segments[i++], CAPABILITY);
+
+ i = 0;
+ while (misc_segments[i].name)
+ parse_test(peer, &misc_segments[i++], CAPABILITY);
+
+ i = 0;
+ while (opt_params[i].name)
+ parse_test(peer, &opt_params[i++], OPT_PARAM);
+
+ SET_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV);
+ peer->connection = bgp_peer_connection_new(peer);
+ peer->connection->status = Established;
+
+ i = 0;
+ while (dynamic_cap_msgs[i].name)
+ parse_test(peer, &dynamic_cap_msgs[i++], DYNCAP);
+
+ printf("failures: %d\n", failed);
+ return failed;
+}
diff --git a/tests/bgpd/test_capability.py b/tests/bgpd/test_capability.py
new file mode 100644
index 0000000..da9245b
--- /dev/null
+++ b/tests/bgpd/test_capability.py
@@ -0,0 +1,56 @@
+import frrtest
+
+
+class TestCapability(frrtest.TestMultiOut):
+ program = "./test_capability"
+
+
+TestCapability.okfail("MP4: MP IP/Uni")
+TestCapability.okfail("MPv6: MP IPv6/Uni")
+TestCapability.okfail("MP2: MP IP/Multicast")
+TestCapability.okfail("MP3: MP IP6/MPLS-labeled VPN")
+TestCapability.okfail("MP5: MP IP6/MPLS-VPN")
+TestCapability.okfail("MP6: MP IP4/MPLS-labeled VPN")
+TestCapability.okfail("MP8: MP unknown AFI/SAFI")
+TestCapability.okfail("MP-short: MP IP4/Unicast, length too short (< minimum)")
+TestCapability.okfail("MP-overflow: MP IP4/Unicast, length too long")
+TestCapability.okfail("caphdr: capability header, and no more")
+TestCapability.okfail("nodata: header, no data but length says there is")
+TestCapability.okfail("padded: valid, with padding")
+TestCapability.okfail("minsize: violates minsize requirement")
+TestCapability.okfail("ORF: ORF, simple, single entry, single tuple")
+TestCapability.okfail("ORF-many: ORF, multi entry/tuple")
+TestCapability.okfail("ORFlo: ORF, multi entry/tuple, hdr length too short")
+TestCapability.okfail("ORFlu: ORF, multi entry/tuple, length too long")
+TestCapability.okfail("ORFnu: ORF, multi entry/tuple, entry number too long")
+TestCapability.okfail("ORFno: ORF, multi entry/tuple, entry number too short")
+TestCapability.okfail("ORFpad: ORF, multi entry/tuple, padded to align")
+TestCapability.okfail("AS4: AS4 capability")
+TestCapability.okfail("GR: GR capability")
+TestCapability.okfail("GR-short: GR capability, but header length too short")
+TestCapability.okfail("GR-long: GR capability, but header length too long")
+TestCapability.okfail("GR-trunc: GR capability, but truncated")
+TestCapability.okfail("GR-empty: GR capability, but empty.")
+TestCapability.okfail("MP-empty: MP capability, but empty.")
+TestCapability.okfail("ORF-empty: ORF capability, but empty.")
+TestCapability.okfail("AS4-empty: AS4 capability, but empty.")
+TestCapability.okfail("dyn-empty: Dynamic capability, but empty.")
+TestCapability.okfail("dyn-old: Dynamic capability (deprecated version)")
+TestCapability.okfail("Role: Role capability")
+TestCapability.okfail("Role-long: Role capability, but too long")
+TestCapability.okfail("Role-empty: Role capability, but empty.")
+TestCapability.okfail("Cap-singlets: One capability per Optional-Param")
+TestCapability.okfail("Cap-series: Series of capability, one Optional-Param")
+TestCapability.okfail("AS4more: AS4 capability after other caps (singlets)")
+TestCapability.okfail("AS4series: AS4 capability, in series of capabilities")
+TestCapability.okfail("AS4real: AS4 capability, in series of capabilities")
+TestCapability.okfail("AS4real2: AS4 capability, in series of capabilities")
+TestCapability.okfail("DynCap: Dynamic Capability Message, IP/Multicast")
+TestCapability.okfail("DynCapLong: Dynamic Capability Message, IP/Multicast, truncated")
+TestCapability.okfail("DynCapPadded: Dynamic Capability Message, IP/Multicast, padded")
+TestCapability.okfail(
+ "DynCapMPCpadded: Dynamic Capability Message, IP/Multicast, cap data padded"
+)
+TestCapability.okfail(
+ "DynCapMPCoverflow: Dynamic Capability Message, IP/Multicast, cap data != length"
+)
diff --git a/tests/bgpd/test_ecommunity.c b/tests/bgpd/test_ecommunity.c
new file mode 100644
index 0000000..49322d3
--- /dev/null
+++ b/tests/bgpd/test_ecommunity.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2007 Sun Microsystems, Inc.
+ */
+#include <zebra.h>
+
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+
+/* need these to link in libbgp */
+struct zebra_privs_t bgpd_privs = {};
+struct event_loop *master = NULL;
+
+static int failed = 0;
+
+/* specification for a test - what the results should be */
+struct test_spec {
+ const char *shouldbe; /* the string the path should parse to */
+};
+
+
+/* test segments to parse and validate, and use for other tests */
+static struct test_segment {
+ const char *name;
+ const char *desc;
+ const uint8_t data[1024];
+ int len;
+ struct test_spec sp;
+} test_segments[] = {{/* 0 */
+ "ipaddr",
+ "rt 1.2.3.4:257",
+ {ECOMMUNITY_ENCODE_IP, ECOMMUNITY_ROUTE_TARGET, 0x1, 0x2,
+ 0x3, 0x4, 0x1, 0x1},
+ 8,
+ {"rt 1.2.3.4:257"}},
+ {/* 1 */
+ "ipaddr-so",
+ "soo 1.2.3.4:257",
+ {ECOMMUNITY_ENCODE_IP, ECOMMUNITY_SITE_ORIGIN, 0x1, 0x2,
+ 0x3, 0x4, 0x1, 0x1},
+ 8,
+ {"soo 1.2.3.4:257"}},
+ {/* 2 */
+ "asn",
+ "rt 23456:987654321",
+ {ECOMMUNITY_ENCODE_AS, ECOMMUNITY_SITE_ORIGIN, 0x5b, 0xa0,
+ 0x3a, 0xde, 0x68, 0xb1},
+ 8,
+ {"soo 23456:987654321"}},
+ {/* 3 */
+ "asn4",
+ "rt 168450976:4321",
+ {ECOMMUNITY_ENCODE_AS4, ECOMMUNITY_SITE_ORIGIN, 0xa, 0xa,
+ 0x5b, 0xa0, 0x10, 0xe1},
+ 8,
+ {"soo 168450976:4321"}},
+ {NULL, NULL, {0}, 0, {NULL}}};
+
+
+/* validate the given aspath */
+static int validate(struct ecommunity *ecom, const struct test_spec *sp)
+{
+ int fails = 0;
+ struct ecommunity *etmp;
+ char *str1, *str2;
+
+ printf("got:\n %s\n", ecommunity_str(ecom));
+ str1 = ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST, 0);
+ etmp = ecommunity_str2com(str1, 0, 1);
+ if (etmp)
+ str2 = ecommunity_ecom2str(etmp,
+ ECOMMUNITY_FORMAT_COMMUNITY_LIST, 0);
+ else
+ str2 = NULL;
+
+ if (strcmp(sp->shouldbe, str1)) {
+ failed++;
+ fails++;
+ printf("shouldbe: %s\n%s\n", str1, sp->shouldbe);
+ }
+ if (!etmp || strcmp(str1, str2)) {
+ failed++;
+ fails++;
+ printf("dogfood: in %s\n"
+ " in->out %s\n",
+ str1, (etmp && str2) ? str2 : "NULL");
+ }
+ ecommunity_free(&etmp);
+ XFREE(MTYPE_ECOMMUNITY_STR, str1);
+ XFREE(MTYPE_ECOMMUNITY_STR, str2);
+
+ return fails;
+}
+
+/* basic parsing test */
+static void parse_test(struct test_segment *t)
+{
+ struct ecommunity *ecom;
+
+ printf("%s: %s\n", t->name, t->desc);
+
+ ecom = ecommunity_parse((uint8_t *)t->data, t->len, 0);
+
+ printf("ecom: %s\nvalidating...:\n", ecommunity_str(ecom));
+
+ if (!validate(ecom, &t->sp))
+ printf("OK\n");
+ else
+ printf("failed\n");
+
+ printf("\n");
+ ecommunity_unintern(&ecom);
+}
+
+
+int main(void)
+{
+ int i = 0;
+ ecommunity_init();
+ while (test_segments[i].name)
+ parse_test(&test_segments[i++]);
+
+ printf("failures: %d\n", failed);
+ // printf ("aspath count: %ld\n", aspath_count());
+ return failed;
+ // return (failed + aspath_count());
+}
diff --git a/tests/bgpd/test_ecommunity.py b/tests/bgpd/test_ecommunity.py
new file mode 100644
index 0000000..1499294
--- /dev/null
+++ b/tests/bgpd/test_ecommunity.py
@@ -0,0 +1,11 @@
+import frrtest
+
+
+class TestEcommunity(frrtest.TestMultiOut):
+ program = "./test_ecommunity"
+
+
+TestEcommunity.okfail("ipaddr")
+TestEcommunity.okfail("ipaddr-so")
+TestEcommunity.okfail("asn")
+TestEcommunity.okfail("asn4")
diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c
new file mode 100644
index 0000000..cebdda9
--- /dev/null
+++ b/tests/bgpd/test_mp_attr.c
@@ -0,0 +1,1117 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2008 Sun Microsystems, Inc.
+ */
+
+#include <zebra.h>
+
+#include "qobj.h"
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_network.h"
+
+#define VT100_RESET "\x1b[0m"
+#define VT100_RED "\x1b[31m"
+#define VT100_GREEN "\x1b[32m"
+#define VT100_YELLOW "\x1b[33m"
+
+#define CAPABILITY 0
+#define DYNCAP 1
+#define OPT_PARAM 2
+
+/* need these to link in libbgp */
+struct zebra_privs_t bgpd_privs = {};
+struct event_loop *master = NULL;
+
+static int failed = 0;
+static int tty = 0;
+
+/* test segments to parse and validate, and use for other tests */
+static struct test_segment {
+ const char *name;
+ const char *desc;
+ const uint8_t data[1024];
+ int len;
+#define SHOULD_PARSE 0
+#define SHOULD_ERR -1
+ int parses; /* whether it should parse or not */
+} mp_reach_segments[] = {
+ {
+ "IPv6",
+ "IPV6 MP Reach, global nexthop, 1 NLRI",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 16,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2,
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ },
+ (4 + 16 + 1 + 5),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-2",
+ "IPV6 MP Reach, global nexthop, 2 NLRIs",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 16,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* ffee:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ },
+ (4 + 16 + 1 + 5 + 9),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-default",
+ "IPV6 MP Reach, global nexthop, 2 NLRIs + default",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 16,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2,
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ 0x0, /* ::/0 */
+ },
+ (4 + 16 + 1 + 5 + 9 + 1),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-lnh",
+ "IPV6 MP Reach, global+local nexthops, 2 NLRIs + default",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 32,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* Nexthop (local) */ 0xfe,
+ 0x80,
+ 0x0,
+ 0x0, /* fe80::210:2ff:.. */
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x2,
+ 0x10,
+ 0x2,
+ 0xff,
+ 0x1,
+ 0x2,
+ 0x3,
+ 0x4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ 0x0, /* ::/0 */
+ },
+ (4 + 32 + 1 + 5 + 9 + 1),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-nhlen",
+ "IPV6 MP Reach, inappropriate nexthop length",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 4,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* Nexthop (local) */ 0xfe,
+ 0x80,
+ 0x0,
+ 0x0, /* fe80::210:2ff:.. */
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x2,
+ 0x10,
+ 0x2,
+ 0xff,
+ 0x1,
+ 0x2,
+ 0x3,
+ 0x4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ 0x0, /* ::/0 */
+ },
+ (4 + 32 + 1 + 5 + 9 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv6-nhlen2",
+ "IPV6 MP Reach, invalid nexthop length",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 5,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* Nexthop (local) */ 0xfe,
+ 0x80,
+ 0x0,
+ 0x0, /* fe80::210:2ff:.. */
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x2,
+ 0x10,
+ 0x2,
+ 0xff,
+ 0x1,
+ 0x2,
+ 0x3,
+ 0x4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ 0x0, /* ::/0 */
+ },
+ (4 + 32 + 1 + 5 + 9 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv6-nhlen3",
+ "IPV6 MP Reach, nexthop length overflow",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 32,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ },
+ (4 + 16),
+ SHOULD_ERR,
+ },
+ {
+ "IPv6-nhlen4",
+ "IPV6 MP Reach, nexthop length short",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 16,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* Nexthop (local) */ 0xfe,
+ 0x80,
+ 0x0,
+ 0x0, /* fe80::210:2ff:.. */
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x2,
+ 0x10,
+ 0x2,
+ 0xff,
+ 0x1,
+ 0x2,
+ 0x3,
+ 0x4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ 0x0, /* ::/0 */
+ },
+ (4 + 32 + 1 + 5 + 9 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv6-nlri",
+ "IPV6 MP Reach, NLRI bitlen overflow",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 32,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* Nexthop (local) */ 0xfe,
+ 0x80,
+ 0x0,
+ 0x0, /* fe80::210:2ff:.. */
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x2,
+ 0x10,
+ 0x2,
+ 0xff,
+ 0x1,
+ 0x2,
+ 0x3,
+ 0x4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 120,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ 0, /* ::/0 */
+ },
+ (4 + 32 + 1 + 5 + 9 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4",
+ "IPv4 MP Reach, 2 NLRIs + default",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
+ /* nexthop bytes */ 4,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
+ 17, 10, 2, 3, /* 10.2.3/17 */
+ 0, /* 0/0 */
+ },
+ (4 + 4 + 1 + 3 + 4 + 1),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv4-nhlen",
+ "IPv4 MP Reach, nexthop lenth overflow",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
+ /* nexthop bytes */ 32,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
+ 17, 10, 2, 3, /* 10.2.3/17 */
+ 0, /* 0/0 */
+ },
+ (4 + 4 + 1 + 3 + 4 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-nlrilen",
+ "IPv4 MP Reach, nlri lenth overflow",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
+ /* nexthop bytes */ 4,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
+ 30, 10, 0, /* 0/0 */
+ },
+ (4 + 4 + 1 + 3 + 2 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-VPNv4",
+ "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 16, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv4-VPNv4-bogus-plen",
+ "IPv4/MPLS-labeled VPN MP Reach, RD, Nexthop, NLRI / bogus p'len",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP,
+ IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0,
+ 0,
+ 1,
+ 2,
+ 0,
+ 0xff,
+ 3,
+ 4,
+ /* Nexthop */ 192,
+ 168,
+ 0,
+ 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 16,
+ 10,
+ 1, /* 10.1/16 */
+ 17,
+ 10,
+ 2,
+ 3, /* 10.2.3/17 */
+ 0, /* 0/0 */
+ },
+ (3 + 1 + 3 * 4 + 1 + 3 + 4 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-VPNv4-plen1-short",
+ "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen short",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 1, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-VPNv4-plen1-long",
+ "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen long",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 32, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-VPNv4-plenn-long",
+ "IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRIs, last plen long",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 16, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ 88 + 1, /* bogus */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3) + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-VPNv4-plenn-short",
+ "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, last plen short",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 16, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 2, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-VPNv4-bogus-rd-type",
+ "IPv4/VPNv4 MP Reach, RD, NH, 2 NLRI, unknown RD in 1st (log, but parse)",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 16, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0xff, 0, /* Bogus RD */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv4-VPNv4-0-nlri",
+ "IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRI, 3rd 0 bogus",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 16, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ 0 /* 0/0, bogus for vpnv4 ?? */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3) + 1),
+ SHOULD_ERR,
+ },
+
+ /* From bug #385 */
+ {
+ "IPv6-bug",
+ "IPv6, global nexthop, 1 default NLRI",
+ {
+ /* AFI / SAFI */ 0x0,
+ 0x2,
+ 0x1,
+ /* nexthop bytes */ 0x20,
+ /* Nexthop (global) */ 0x20,
+ 0x01,
+ 0x04,
+ 0x70,
+ 0x00,
+ 0x01,
+ 0x00,
+ 0x06,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x01,
+ /* Nexthop (local) */ 0xfe,
+ 0x80,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x02,
+ 0x0c,
+ 0xdb,
+ 0xff,
+ 0xfe,
+ 0xfe,
+ 0xeb,
+ 0x00,
+ /* SNPA (defunct, MBZ) */ 0,
+ /* NLRI tuples */ /* Should have 0 here for ::/0, but
+ dont */
+ },
+ 37,
+ SHOULD_ERR,
+ },
+ {
+ .name = "IPv4",
+ .desc = "IPV4 MP Reach, flowspec, 1 NLRI",
+ .data = {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP,
+ IANA_SAFI_FLOWSPEC,
+ 0x00, /* no NH */
+ 0x00,
+ 0x06, /* FS Length */
+ 0x01, /* FS dest prefix ID */
+ 0x1e, /* IP */
+ 0x1e,
+ 0x28,
+ 0x28,
+ 0x0
+ },
+ .len = 12,
+ .parses = SHOULD_PARSE,
+ },
+ {NULL, NULL, {0}, 0, 0}};
+
+/* MP_UNREACH_NLRI tests */
+static struct test_segment mp_unreach_segments[] = {
+ {
+ "IPv6-unreach",
+ "IPV6 MP Unreach, 1 NLRI",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
+ /* NLRI tuples */ 32, 0xff, 0xfe, 0x1,
+ 0x2, /* fffe:102::/32 */
+ },
+ (3 + 5),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-unreach2",
+ "IPV6 MP Unreach, 2 NLRIs",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
+ /* NLRI tuples */ 32, 0xff, 0xfe, 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64, 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+ 0x0, 0x2, 0x0, 0x3,
+ },
+ (3 + 5 + 9),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-unreach-default",
+ "IPV6 MP Unreach, 2 NLRIs + default",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
+ /* NLRI tuples */ 32, 0xff, 0xfe, 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64, 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+ 0x0, 0x2, 0x0, 0x3, 0x0, /* ::/0 */
+ },
+ (3 + 5 + 9 + 1),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-unreach-nlri",
+ "IPV6 MP Unreach, NLRI bitlen overflow",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
+ /* NLRI tuples */ 120, 0xff, 0xfe, 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64, 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+ 0x0, 0x2, 0x0, 0x3, 0, /* ::/0 */
+ },
+ (3 + 5 + 9 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-unreach",
+ "IPv4 MP Unreach, 2 NLRIs + default",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
+ /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
+ 17, 10, 2, 3, /* 10.2.3/17 */
+ 0, /* 0/0 */
+ },
+ (3 + 3 + 4 + 1),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv4-unreach-nlrilen",
+ "IPv4 MP Unreach, nlri length overflow",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
+ /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
+ 30, 10, 0, /* 0/0 */
+ },
+ (3 + 3 + 2 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-unreach-VPNv4",
+ "IPv4/MPLS-labeled VPN MP Unreach, RD, 3 NLRIs",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* NLRI tuples */ 88 + 16, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ },
+ (3 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
+ SHOULD_PARSE,
+ },
+ {
+ .name = "IPv4",
+ .desc = "IPV4 MP Unreach, flowspec, 1 NLRI",
+ .data = {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP,
+ IANA_SAFI_FLOWSPEC,
+ 0x06, /* FS Length */
+ 0x01, /* FS dest prefix ID */
+ 0x1e, /* IP */
+ 0x1e,
+ 0x28,
+ 0x28,
+ 0x0
+ },
+ .len = 10,
+ .parses = SHOULD_PARSE,
+ },
+ {NULL, NULL, {0}, 0, 0}};
+
+static struct test_segment mp_prefix_sid[] = {
+ {
+ "PREFIX-SID",
+ "PREFIX-SID Test 1",
+ {
+ /* TLV[0] Latel-Index TLV */
+ 0x01, /* Type 0x01:Label-Index */
+ 0x00, 0x07, /* Length */
+ 0x00, /* RESERVED */
+ 0x00, 0x00, /* Flags */
+ 0x00, 0x00, 0x00, 0x02, /* Label Index */
+
+ /* TLV[1] SRGB TLV */
+ 0x03, /* Type 0x03:SRGB */
+ 0x00, 0x08, /* Length */
+ 0x00, 0x00, /* Flags */
+ 0x0a, 0x1b, 0xfe, /* SRGB[0] first label */
+ 0x00, 0x00, 0x0a /* SRBG[0] nb-labels in range */
+ },
+ .len = 21,
+ .parses = SHOULD_PARSE,
+ },
+ {NULL, NULL, { 0 }, 0, 0},
+};
+
+/* nlri_parse indicates 0 on successful parse, and -1 otherwise.
+ * attr_parse indicates BGP_ATTR_PARSE_PROCEED/0 on success,
+ * and BGP_ATTR_PARSE_ERROR/-1 or lower negative ret on err.
+ */
+static void handle_result(struct peer *peer, struct test_segment *t,
+ int parse_ret, int nlri_ret)
+{
+ int oldfailed = failed;
+
+ printf("mp attr parsed?: %s\n", parse_ret ? "no" : "yes");
+ if (!parse_ret)
+ printf("nrli parsed?: %s\n", nlri_ret ? "no" : "yes");
+ printf("should parse?: %s\n", t->parses ? "no" : "yes");
+
+ if ((parse_ret != 0 || nlri_ret != 0) != (t->parses != 0))
+ failed++;
+
+
+ if (tty)
+ printf("%s",
+ (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET
+ : VT100_GREEN "OK" VT100_RESET);
+ else
+ printf("%s", (failed > oldfailed) ? "failed!" : "OK");
+
+ if (failed)
+ printf(" (%u)", failed);
+
+ printf("\n\n");
+}
+
+/* basic parsing test */
+static void parse_test(struct peer *peer, struct test_segment *t, int type)
+{
+ int parse_ret = 0, nlri_ret = 0;
+ struct attr attr = {};
+ struct bgp_nlri nlri = {};
+ struct bgp_attr_parser_args attr_args = {
+ .peer = peer,
+ .length = t->len,
+ .total = 1,
+ .attr = &attr,
+ .type = type,
+ .flags = BGP_ATTR_FLAG_OPTIONAL,
+ .startp = BGP_INPUT_PNT(peer),
+ };
+#define RANDOM_FUZZ 35
+ stream_reset(peer->curr);
+ stream_put(peer->curr, NULL, RANDOM_FUZZ);
+ stream_set_getp(peer->curr, RANDOM_FUZZ);
+
+ stream_write(peer->curr, t->data, t->len);
+
+ printf("%s: %s\n", t->name, t->desc);
+
+ switch (type) {
+ case BGP_ATTR_MP_REACH_NLRI:
+ parse_ret = bgp_mp_reach_parse(&attr_args, &nlri);
+ break;
+ case BGP_ATTR_MP_UNREACH_NLRI:
+ parse_ret = bgp_mp_unreach_parse(&attr_args, &nlri);
+ break;
+ case BGP_ATTR_PREFIX_SID:
+ parse_ret = bgp_attr_prefix_sid(&attr_args);
+ break;
+ default:
+ printf("unknown type");
+ return;
+ }
+ if (!parse_ret) {
+ iana_afi_t pkt_afi;
+ iana_safi_t pkt_safi;
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_int2iana(nlri.afi, nlri.safi, &pkt_afi,
+ &pkt_safi))
+ assert(0);
+
+ printf("MP: %u(%u)/%u(%u): recv %u, nego %u\n", nlri.afi,
+ pkt_afi, nlri.safi, pkt_safi,
+ peer->afc_recv[nlri.afi][nlri.safi],
+ peer->afc_nego[nlri.afi][nlri.safi]);
+ }
+
+ if (!parse_ret) {
+ if (type == BGP_ATTR_MP_REACH_NLRI)
+ nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, false);
+ else if (type == BGP_ATTR_MP_UNREACH_NLRI)
+ nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, true);
+ }
+ handle_result(peer, t, parse_ret, nlri_ret);
+}
+
+static struct bgp *bgp;
+static as_t asn = 100;
+
+int main(void)
+{
+ struct interface ifp;
+ struct peer *peer;
+ int i, j;
+
+ conf_bgp_debug_neighbor_events = -1UL;
+ conf_bgp_debug_packet = -1UL;
+ conf_bgp_debug_as4 = -1UL;
+ conf_bgp_debug_flowspec = -1UL;
+ term_bgp_debug_neighbor_events = -1UL;
+ term_bgp_debug_packet = -1UL;
+ term_bgp_debug_as4 = -1UL;
+ term_bgp_debug_flowspec = -1UL;
+
+ qobj_init();
+ cmd_init(0);
+ bgp_vty_init();
+ master = event_master_create("test mp attr");
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
+ vrf_init(NULL, NULL, NULL, NULL);
+ bgp_option_set(BGP_OPT_NO_LISTEN);
+ bgp_attr_init();
+
+ if (fileno(stdout) >= 0)
+ tty = isatty(fileno(stdout));
+
+ if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, NULL,
+ ASNOTATION_PLAIN) < 0)
+ return -1;
+
+ peer = peer_create_accept(bgp);
+ peer->host = (char *)"foo";
+ peer->connection = bgp_peer_connection_new(peer);
+ peer->connection->status = Established;
+ peer->curr = stream_new(BGP_MAX_PACKET_SIZE);
+
+ ifp.ifindex = 0;
+ peer->nexthop.ifp = &ifp;
+
+ for (i = AFI_IP; i < AFI_MAX; i++)
+ for (j = SAFI_UNICAST; j < SAFI_MAX; j++) {
+ peer->afc[i][j] = 1;
+ peer->afc_adv[i][j] = 1;
+ }
+
+ i = 0;
+ while (mp_reach_segments[i].name)
+ parse_test(peer, &mp_reach_segments[i++],
+ BGP_ATTR_MP_REACH_NLRI);
+
+ i = 0;
+ while (mp_unreach_segments[i].name)
+ parse_test(peer, &mp_unreach_segments[i++],
+ BGP_ATTR_MP_UNREACH_NLRI);
+
+ i = 0;
+ while (mp_prefix_sid[i].name)
+ parse_test(peer, &mp_prefix_sid[i++],
+ BGP_ATTR_PREFIX_SID);
+ printf("failures: %d\n", failed);
+ return failed;
+}
diff --git a/tests/bgpd/test_mp_attr.py b/tests/bgpd/test_mp_attr.py
new file mode 100644
index 0000000..d9612bb
--- /dev/null
+++ b/tests/bgpd/test_mp_attr.py
@@ -0,0 +1,49 @@
+import frrtest
+
+
+class TestMpAttr(frrtest.TestMultiOut):
+ program = "./test_mp_attr"
+
+
+TestMpAttr.okfail("IPv6: IPV6 MP Reach, global nexthop, 1 NLRI")
+TestMpAttr.okfail("IPv6-2: IPV6 MP Reach, global nexthop, 2 NLRIs")
+TestMpAttr.okfail("IPv6-default: IPV6 MP Reach, global nexthop, 2 NLRIs + default")
+TestMpAttr.okfail("IPv6-lnh: IPV6 MP Reach, global+local nexthops, 2 NLRIs + default")
+TestMpAttr.okfail("IPv6-nhlen: IPV6 MP Reach, inappropriate nexthop length")
+TestMpAttr.okfail("IPv6-nhlen2: IPV6 MP Reach, invalid nexthop length")
+TestMpAttr.okfail("IPv6-nhlen3: IPV6 MP Reach, nexthop length overflow")
+TestMpAttr.okfail("IPv6-nhlen4: IPV6 MP Reach, nexthop length short")
+TestMpAttr.okfail("IPv6-nlri: IPV6 MP Reach, NLRI bitlen overflow")
+TestMpAttr.okfail("IPv4: IPv4 MP Reach, 2 NLRIs + default")
+TestMpAttr.okfail("IPv4-nhlen: IPv4 MP Reach, nexthop lenth overflow")
+TestMpAttr.okfail("IPv4-nlrilen: IPv4 MP Reach, nlri lenth overflow")
+TestMpAttr.okfail("IPv4-VPNv4: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs")
+TestMpAttr.okfail(
+ "IPv4-VPNv4-bogus-plen: IPv4/MPLS-labeled VPN MP Reach, RD, Nexthop, NLRI / bogus p'len"
+)
+TestMpAttr.okfail(
+ "IPv4-VPNv4-plen1-short: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen short"
+)
+TestMpAttr.okfail(
+ "IPv4-VPNv4-plen1-long: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen long"
+)
+TestMpAttr.okfail(
+ "IPv4-VPNv4-plenn-long: IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRIs, last plen long"
+)
+TestMpAttr.okfail(
+ "IPv4-VPNv4-plenn-short: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, last plen short"
+)
+TestMpAttr.okfail(
+ "IPv4-VPNv4-bogus-rd-type: IPv4/VPNv4 MP Reach, RD, NH, 2 NLRI, unknown RD in 1st (log, but parse)"
+)
+TestMpAttr.okfail(
+ "IPv4-VPNv4-0-nlri: IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRI, 3rd 0 bogus"
+)
+TestMpAttr.okfail("IPv6-bug: IPv6, global nexthop, 1 default NLRI")
+TestMpAttr.okfail("IPv6-unreach: IPV6 MP Unreach, 1 NLRI")
+TestMpAttr.okfail("IPv6-unreach2: IPV6 MP Unreach, 2 NLRIs")
+TestMpAttr.okfail("IPv6-unreach-default: IPV6 MP Unreach, 2 NLRIs + default")
+TestMpAttr.okfail("IPv6-unreach-nlri: IPV6 MP Unreach, NLRI bitlen overflow")
+TestMpAttr.okfail("IPv4-unreach: IPv4 MP Unreach, 2 NLRIs + default")
+TestMpAttr.okfail("IPv4-unreach-nlrilen: IPv4 MP Unreach, nlri length overflow")
+TestMpAttr.okfail("IPv4-unreach-VPNv4: IPv4/MPLS-labeled VPN MP Unreach, RD, 3 NLRIs")
diff --git a/tests/bgpd/test_mpath.c b/tests/bgpd/test_mpath.c
new file mode 100644
index 0000000..ebbe3ac
--- /dev/null
+++ b/tests/bgpd/test_mpath.c
@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BGP Multipath Unit Test
+ * Copyright (C) 2010 Google Inc.
+ *
+ * This file is part of Quagga
+ */
+
+#include <zebra.h>
+
+#include "qobj.h"
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "linklist.h"
+#include "memory.h"
+#include "zclient.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_network.h"
+
+#define VT100_RESET "\x1b[0m"
+#define VT100_RED "\x1b[31m"
+#define VT100_GREEN "\x1b[32m"
+#define VT100_YELLOW "\x1b[33m"
+#define OK VT100_GREEN "OK" VT100_RESET
+#define FAILED VT100_RED "failed" VT100_RESET
+
+#define TEST_PASSED 0
+#define TEST_FAILED -1
+
+#define EXPECT_TRUE(expr, res) \
+ if (!(expr)) { \
+ printf("Test failure in %s line %u: %s\n", __func__, __LINE__, \
+ #expr); \
+ (res) = TEST_FAILED; \
+ }
+
+typedef struct testcase_t__ testcase_t;
+
+typedef int (*test_setup_func)(testcase_t *);
+typedef int (*test_run_func)(testcase_t *);
+typedef int (*test_cleanup_func)(testcase_t *);
+
+struct testcase_t__ {
+ const char *desc;
+ void *test_data;
+ void *verify_data;
+ void *tmp_data;
+ test_setup_func setup;
+ test_run_func run;
+ test_cleanup_func cleanup;
+};
+
+/* need these to link in libbgp */
+struct event_loop *master = NULL;
+extern struct zclient *zclient;
+struct zebra_privs_t bgpd_privs = {
+ .user = NULL,
+ .group = NULL,
+ .vty_group = NULL,
+};
+
+static int tty = 0;
+
+/* Create fake bgp instance */
+static struct bgp *bgp_create_fake(as_t *as, const char *name)
+{
+ struct bgp *bgp;
+ afi_t afi;
+ safi_t safi;
+
+ if ((bgp = XCALLOC(MTYPE_BGP, sizeof(struct bgp))) == NULL)
+ return NULL;
+
+ bgp_lock(bgp);
+ // bgp->peer_self = peer_new (bgp);
+ // bgp->peer_self->host = XSTRDUP (MTYPE_BGP_PEER_HOST, "Static
+ // announcement");
+
+ bgp->peer = list_new();
+ // bgp->peer->cmp = (int (*)(void *, void *)) peer_cmp;
+
+ bgp->group = list_new();
+ // bgp->group->cmp = (int (*)(void *, void *)) peer_group_cmp;
+
+ bgp_evpn_init(bgp);
+ FOREACH_AFI_SAFI (afi, safi) {
+ bgp->route[afi][safi] = bgp_table_init(bgp, afi, safi);
+ bgp->aggregate[afi][safi] = bgp_table_init(bgp, afi, safi);
+ bgp->rib[afi][safi] = bgp_table_init(bgp, afi, safi);
+ bgp->maxpaths[afi][safi].maxpaths_ebgp = MULTIPATH_NUM;
+ bgp->maxpaths[afi][safi].maxpaths_ibgp = MULTIPATH_NUM;
+ }
+
+ bgp_scan_init(bgp);
+ bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
+ bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
+ bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
+ bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
+ bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
+
+ bgp->as = *as;
+
+ if (name)
+ bgp->name = strdup(name);
+
+ return bgp;
+}
+
+/*=========================================================
+ * Testcase for maximum-paths configuration
+ */
+static int setup_bgp_cfg_maximum_paths(testcase_t *t)
+{
+ as_t asn = 1;
+ t->tmp_data = bgp_create_fake(&asn, NULL);
+ if (!t->tmp_data)
+ return -1;
+ return 0;
+}
+
+static int run_bgp_cfg_maximum_paths(testcase_t *t)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp *bgp;
+ int api_result;
+ int test_result = TEST_PASSED;
+
+ bgp = t->tmp_data;
+ FOREACH_AFI_SAFI (afi, safi) {
+ /* test bgp_maximum_paths_set */
+ api_result = bgp_maximum_paths_set(bgp, afi, safi,
+ BGP_PEER_EBGP, 10, 0);
+ EXPECT_TRUE(api_result == 0, test_result);
+ api_result = bgp_maximum_paths_set(bgp, afi, safi,
+ BGP_PEER_IBGP, 10, 0);
+ EXPECT_TRUE(api_result == 0, test_result);
+ EXPECT_TRUE(bgp->maxpaths[afi][safi].maxpaths_ebgp == 10,
+ test_result);
+ EXPECT_TRUE(bgp->maxpaths[afi][safi].maxpaths_ibgp == 10,
+ test_result);
+
+ /* test bgp_maximum_paths_unset */
+ api_result =
+ bgp_maximum_paths_unset(bgp, afi, safi, BGP_PEER_EBGP);
+ EXPECT_TRUE(api_result == 0, test_result);
+ api_result =
+ bgp_maximum_paths_unset(bgp, afi, safi, BGP_PEER_IBGP);
+ EXPECT_TRUE(api_result == 0, test_result);
+ EXPECT_TRUE((bgp->maxpaths[afi][safi].maxpaths_ebgp
+ == MULTIPATH_NUM),
+ test_result);
+ EXPECT_TRUE((bgp->maxpaths[afi][safi].maxpaths_ibgp
+ == MULTIPATH_NUM),
+ test_result);
+ }
+
+ return test_result;
+}
+
+static int cleanup_bgp_cfg_maximum_paths(testcase_t *t)
+{
+ return bgp_delete((struct bgp *)t->tmp_data);
+}
+
+testcase_t test_bgp_cfg_maximum_paths = {
+ .desc = "Test bgp maximum-paths config",
+ .setup = setup_bgp_cfg_maximum_paths,
+ .run = run_bgp_cfg_maximum_paths,
+ .cleanup = cleanup_bgp_cfg_maximum_paths,
+};
+
+/*=========================================================
+ * Testcase for bgp_mp_list
+ */
+struct peer test_mp_list_peer[] = {
+ {.local_as = 1, .as = 2}, {.local_as = 1, .as = 2},
+ {.local_as = 1, .as = 2}, {.local_as = 1, .as = 2},
+ {.local_as = 1, .as = 2},
+};
+int test_mp_list_peer_count = array_size(test_mp_list_peer);
+struct attr test_mp_list_attr[4];
+struct bgp_path_info test_mp_list_info[] = {
+ {.peer = &test_mp_list_peer[0], .attr = &test_mp_list_attr[0]},
+ {.peer = &test_mp_list_peer[1], .attr = &test_mp_list_attr[1]},
+ {.peer = &test_mp_list_peer[2], .attr = &test_mp_list_attr[1]},
+ {.peer = &test_mp_list_peer[3], .attr = &test_mp_list_attr[2]},
+ {.peer = &test_mp_list_peer[4], .attr = &test_mp_list_attr[3]},
+};
+int test_mp_list_info_count = array_size(test_mp_list_info);
+
+static int setup_bgp_mp_list(testcase_t *t)
+{
+ test_mp_list_attr[0].nexthop.s_addr = 0x01010101;
+ test_mp_list_attr[1].nexthop.s_addr = 0x02020202;
+ test_mp_list_attr[2].nexthop.s_addr = 0x03030303;
+ test_mp_list_attr[3].nexthop.s_addr = 0x04040404;
+
+ if ((test_mp_list_peer[0].su_remote = sockunion_str2su("1.1.1.1"))
+ == NULL)
+ return -1;
+ if ((test_mp_list_peer[1].su_remote = sockunion_str2su("2.2.2.2"))
+ == NULL)
+ return -1;
+ if ((test_mp_list_peer[2].su_remote = sockunion_str2su("3.3.3.3"))
+ == NULL)
+ return -1;
+ if ((test_mp_list_peer[3].su_remote = sockunion_str2su("4.4.4.4"))
+ == NULL)
+ return -1;
+ if ((test_mp_list_peer[4].su_remote = sockunion_str2su("5.5.5.5"))
+ == NULL)
+ return -1;
+
+ return 0;
+}
+
+static int run_bgp_mp_list(testcase_t *t)
+{
+ struct list mp_list;
+ struct listnode *mp_node;
+ struct bgp_path_info *info;
+ int i;
+ int test_result = TEST_PASSED;
+ bgp_mp_list_init(&mp_list);
+ EXPECT_TRUE(listcount(&mp_list) == 0, test_result);
+
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[1]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[4]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[2]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[3]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[0]);
+
+ for (i = 0, mp_node = mp_list.head; i < test_mp_list_info_count;
+ i++, mp_node = listnextnode(mp_node)) {
+ info = listgetdata(mp_node);
+ info->lock++;
+ EXPECT_TRUE(info == &test_mp_list_info[i], test_result);
+ }
+
+ bgp_mp_list_clear(&mp_list);
+ EXPECT_TRUE(listcount(&mp_list) == 0, test_result);
+
+ return test_result;
+}
+
+static int cleanup_bgp_mp_list(testcase_t *t)
+{
+ int i;
+
+ for (i = 0; i < test_mp_list_peer_count; i++)
+ sockunion_free(test_mp_list_peer[i].su_remote);
+
+ return 0;
+}
+
+testcase_t test_bgp_mp_list = {
+ .desc = "Test bgp_mp_list",
+ .setup = setup_bgp_mp_list,
+ .run = run_bgp_mp_list,
+ .cleanup = cleanup_bgp_mp_list,
+};
+
+/*=========================================================
+ * Testcase for bgp_path_info_mpath_update
+ */
+
+static struct bgp_dest *dest;
+
+static int setup_bgp_path_info_mpath_update(testcase_t *t)
+{
+ int i;
+ struct bgp *bgp;
+ struct bgp_table *rt;
+ struct prefix p;
+ as_t asn = 1;
+
+ t->tmp_data = bgp_create_fake(&asn, NULL);
+ if (!t->tmp_data)
+ return -1;
+
+ bgp = t->tmp_data;
+ rt = bgp->rib[AFI_IP][SAFI_UNICAST];
+
+ if (!rt)
+ return -1;
+
+ str2prefix("42.1.1.0/24", &p);
+ dest = bgp_node_get(rt, &p);
+
+ setup_bgp_mp_list(t);
+ for (i = 0; i < test_mp_list_info_count; i++)
+ bgp_path_info_add(dest, &test_mp_list_info[i]);
+ return 0;
+}
+
+static int run_bgp_path_info_mpath_update(testcase_t *t)
+{
+ struct bgp_path_info *new_best, *old_best, *mpath;
+ struct list mp_list;
+ struct bgp_maxpaths_cfg mp_cfg = {3, 3};
+
+ int test_result = TEST_PASSED;
+ bgp_mp_list_init(&mp_list);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[4]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[3]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[0]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[1]);
+ new_best = &test_mp_list_info[3];
+ old_best = NULL;
+ bgp_path_info_mpath_update(NULL, dest, new_best, old_best, &mp_list,
+ &mp_cfg);
+ bgp_mp_list_clear(&mp_list);
+ EXPECT_TRUE(bgp_path_info_mpath_count(new_best) == 2, test_result);
+ mpath = bgp_path_info_mpath_first(new_best);
+ EXPECT_TRUE(mpath == &test_mp_list_info[0], test_result);
+ EXPECT_TRUE(CHECK_FLAG(mpath->flags, BGP_PATH_MULTIPATH), test_result);
+ mpath = bgp_path_info_mpath_next(mpath);
+ EXPECT_TRUE(mpath == &test_mp_list_info[1], test_result);
+ EXPECT_TRUE(CHECK_FLAG(mpath->flags, BGP_PATH_MULTIPATH), test_result);
+
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[0]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[1]);
+ new_best = &test_mp_list_info[0];
+ old_best = &test_mp_list_info[3];
+ bgp_path_info_mpath_update(NULL, dest, new_best, old_best, &mp_list,
+ &mp_cfg);
+ bgp_mp_list_clear(&mp_list);
+ EXPECT_TRUE(bgp_path_info_mpath_count(new_best) == 1, test_result);
+ mpath = bgp_path_info_mpath_first(new_best);
+ EXPECT_TRUE(mpath == &test_mp_list_info[1], test_result);
+ EXPECT_TRUE(CHECK_FLAG(mpath->flags, BGP_PATH_MULTIPATH), test_result);
+ EXPECT_TRUE(!CHECK_FLAG(test_mp_list_info[0].flags, BGP_PATH_MULTIPATH),
+ test_result);
+
+ return test_result;
+}
+
+static int cleanup_bgp_path_info_mpath_update(testcase_t *t)
+{
+ int i;
+
+ for (i = 0; i < test_mp_list_peer_count; i++)
+ sockunion_free(test_mp_list_peer[i].su_remote);
+
+ return bgp_delete((struct bgp *)t->tmp_data);
+}
+
+testcase_t test_bgp_path_info_mpath_update = {
+ .desc = "Test bgp_path_info_mpath_update",
+ .setup = setup_bgp_path_info_mpath_update,
+ .run = run_bgp_path_info_mpath_update,
+ .cleanup = cleanup_bgp_path_info_mpath_update,
+};
+
+/*=========================================================
+ * Set up testcase vector
+ */
+testcase_t *all_tests[] = {
+ &test_bgp_cfg_maximum_paths, &test_bgp_mp_list,
+ &test_bgp_path_info_mpath_update,
+};
+
+int all_tests_count = array_size(all_tests);
+
+/*=========================================================
+ * Test Driver Functions
+ */
+static int global_test_init(void)
+{
+ qobj_init();
+ master = event_master_create(NULL);
+ zclient = zclient_new(master, &zclient_options_default, NULL, 0);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
+ vrf_init(NULL, NULL, NULL, NULL);
+ bgp_option_set(BGP_OPT_NO_LISTEN);
+
+ if (fileno(stdout) >= 0)
+ tty = isatty(fileno(stdout));
+ return 0;
+}
+
+static int global_test_cleanup(void)
+{
+ if (zclient != NULL)
+ zclient_free(zclient);
+ event_master_free(master);
+ return 0;
+}
+
+static void display_result(testcase_t *test, int result)
+{
+ if (tty)
+ printf("%s: %s\n", test->desc,
+ result == TEST_PASSED ? OK : FAILED);
+ else
+ printf("%s: %s\n", test->desc,
+ result == TEST_PASSED ? "OK" : "FAILED");
+}
+
+static int setup_test(testcase_t *t)
+{
+ int res = 0;
+ if (t->setup)
+ res = t->setup(t);
+ return res;
+}
+
+static int cleanup_test(testcase_t *t)
+{
+ int res = 0;
+ if (t->cleanup)
+ res = t->cleanup(t);
+ return res;
+}
+
+static void run_tests(testcase_t *tests[], int num_tests, int *pass_count,
+ int *fail_count)
+{
+ int test_index, result;
+ testcase_t *cur_test;
+
+ *pass_count = *fail_count = 0;
+
+ for (test_index = 0; test_index < num_tests; test_index++) {
+ cur_test = tests[test_index];
+ if (!cur_test->desc) {
+ printf("error: test %d has no description!\n",
+ test_index);
+ continue;
+ }
+ if (!cur_test->run) {
+ printf("error: test %s has no run function!\n",
+ cur_test->desc);
+ continue;
+ }
+ if (setup_test(cur_test) != 0) {
+ printf("error: setup failed for test %s\n",
+ cur_test->desc);
+ continue;
+ }
+ result = cur_test->run(cur_test);
+ if (result == TEST_PASSED)
+ *pass_count += 1;
+ else
+ *fail_count += 1;
+ display_result(cur_test, result);
+ if (cleanup_test(cur_test) != 0) {
+ printf("error: cleanup failed for test %s\n",
+ cur_test->desc);
+ continue;
+ }
+ }
+}
+
+int main(void)
+{
+ int pass_count, fail_count;
+ time_t cur_time;
+ char buf[32];
+
+ time(&cur_time);
+ printf("BGP Multipath Tests Run at %s", ctime_r(&cur_time, buf));
+ if (global_test_init() != 0) {
+ printf("Global init failed. Terminating.\n");
+ exit(1);
+ }
+ run_tests(all_tests, all_tests_count, &pass_count, &fail_count);
+ global_test_cleanup();
+ printf("Total pass/fail: %d/%d\n", pass_count, fail_count);
+ return fail_count;
+}
diff --git a/tests/bgpd/test_mpath.py b/tests/bgpd/test_mpath.py
new file mode 100644
index 0000000..582fd25
--- /dev/null
+++ b/tests/bgpd/test_mpath.py
@@ -0,0 +1,10 @@
+import frrtest
+
+
+class TestMpath(frrtest.TestMultiOut):
+ program = "./test_mpath"
+
+
+TestMpath.okfail("bgp maximum-paths config")
+TestMpath.okfail("bgp_mp_list")
+TestMpath.okfail("bgp_path_info_mpath_update")
diff --git a/tests/bgpd/test_packet.c b/tests/bgpd/test_packet.c
new file mode 100644
index 0000000..a83276b
--- /dev/null
+++ b/tests/bgpd/test_packet.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Cumulus Networks Inc.
+ * Donald Sharp
+ *
+ * This file is part of FRR
+ */
+
+#include <zebra.h>
+
+#include "qobj.h"
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_network.h"
+
+/* need these to link in libbgp */
+struct zebra_privs_t bgpd_privs = {};
+struct event_loop *master = NULL;
+
+static struct bgp *bgp;
+static as_t asn = 100;
+
+extern int bgp_read_packet(struct peer *peer);
+
+/*
+ * This file is intended to be used as input for some sort of
+ * fuzzer. Specifically I had afl in mind when I wrote
+ * this code.
+ */
+int main(int argc, char *argv[])
+{
+ struct peer *peer;
+ int i, j;
+ struct event t;
+
+ qobj_init();
+ bgp_attr_init();
+ master = event_master_create(NULL);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
+ vrf_init(NULL, NULL, NULL, NULL);
+ bgp_option_set(BGP_OPT_NO_LISTEN);
+
+ if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, NULL,
+ ASNOTATION_PLAIN) < 0)
+ return -1;
+
+ peer = peer_create_accept(bgp);
+ peer->host = (char *)"foo";
+
+ for (i = AFI_IP; i < AFI_MAX; i++)
+ for (j = SAFI_UNICAST; j < SAFI_MAX; j++) {
+ peer->afc[i][j] = 1;
+ peer->afc_adv[i][j] = 1;
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV);
+ peer->connection = bgp_peer_connection_new(peer);
+ peer->connection->status = Established;
+
+ peer->connection->fd = open(argv[1], O_RDONLY | O_NONBLOCK);
+ t.arg = peer;
+ peer->connection->t_read = &t;
+
+ // printf("bgp_read_packet returns: %d\n", bgp_read(&t));
+}
diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c
new file mode 100644
index 0000000..bc6eba9
--- /dev/null
+++ b/tests/bgpd/test_peer_attr.c
@@ -0,0 +1,1491 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BGP Peer Attribute Unit Tests
+ * Copyright (C) 2018 Pascal Mathis
+ */
+#include <zebra.h>
+
+#include "memory.h"
+#include "plist.h"
+#include "printfrr.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_clist.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_filter.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_network.h"
+
+#ifdef ENABLE_BGP_VNC
+#include "bgpd/rfapi/rfapi_backend.h"
+#endif
+
+#define OUT_SYMBOL_INFO "\u25ba"
+#define OUT_SYMBOL_OK "\u2714"
+#define OUT_SYMBOL_NOK "\u2716"
+
+#define TEST_ASSERT(T, C) \
+ do { \
+ if ((T)->state != TEST_SUCCESS || (C)) \
+ break; \
+ (T)->state = TEST_ASSERT_ERROR; \
+ (T)->error = \
+ asprintfrr(MTYPE_TMP, "assertion failed: %s (%s:%d)", \
+ (#C), __FILE__, __LINE__); \
+ } while (0)
+
+#define TEST_ASSERT_EQ(T, A, B) \
+ do { \
+ if ((T)->state != TEST_SUCCESS || ((A) == (B))) \
+ break; \
+ (T)->state = TEST_ASSERT_ERROR; \
+ (T)->error = asprintfrr( \
+ MTYPE_TMP, \
+ "assertion failed: %s[%lld] == [%lld]%s (%s:%d)", \
+ (#A), (long long)(A), (long long)(B), (#B), __FILE__, \
+ __LINE__); \
+ } while (0)
+
+#define TEST_HANDLER_MAX 5
+#define TEST_HANDLER(name) _test_handler_##name
+#define TEST_HANDLER_DECL(name) \
+ static void _test_handler_##name( \
+ struct test *test, struct test_peer_attr *pa, \
+ struct peer *peer, struct peer *group, bool peer_set, \
+ bool group_set)
+
+#define TEST_ATTR_HANDLER_DECL(name, attr, pval, gval) \
+ TEST_HANDLER_DECL(name) \
+ { \
+ if (peer_set) \
+ TEST_ASSERT_EQ(test, peer->attr, (pval)); \
+ else if (peer_group_active(peer) && group_set) \
+ TEST_ASSERT_EQ(test, peer->attr, (gval)); \
+ if (group_set) \
+ TEST_ASSERT_EQ(test, group->attr, (gval)); \
+ } \
+ TEST_HANDLER_DECL(name)
+
+#define TEST_STR_ATTR_HANDLER_DECL(name, attr, pval, gval) \
+ TEST_HANDLER_DECL(name) \
+ { \
+ if (peer_set) { \
+ TEST_ASSERT(test, peer->attr != NULL); \
+ TEST_ASSERT(test, !strcmp(peer->attr, (pval))); \
+ } else if (peer_group_active(peer) && group_set) { \
+ TEST_ASSERT(test, peer->attr != NULL); \
+ TEST_ASSERT(test, !strcmp(peer->attr, (gval))); \
+ } \
+ if (group_set) { \
+ TEST_ASSERT(test, group->attr != NULL); \
+ TEST_ASSERT(test, !strcmp(group->attr, (gval))); \
+ } \
+ } \
+ TEST_HANDLER_DECL(name)
+
+#define TEST_SU_ATTR_HANDLER_DECL(name, attr, pval, gval) \
+ TEST_HANDLER_DECL(name) \
+ { \
+ union sockunion su; \
+ if (peer_set) { \
+ str2sockunion(pval, &su); \
+ TEST_ASSERT(test, !sockunion_cmp(peer->attr, &su)); \
+ } else if (peer_group_active(peer) && group_set) { \
+ str2sockunion(gval, &su); \
+ TEST_ASSERT(test, !sockunion_cmp(group->attr, &su)); \
+ } \
+ if (group_set) { \
+ str2sockunion(gval, &su); \
+ TEST_ASSERT(test, !sockunion_cmp(group->attr, &su)); \
+ } \
+ } \
+ TEST_HANDLER_DECL(name)
+
+/* Required variables to link in libbgp */
+struct zebra_privs_t bgpd_privs = {0};
+struct event_loop *master;
+
+enum test_state {
+ TEST_SUCCESS,
+ TEST_SKIPPING,
+ TEST_COMMAND_ERROR,
+ TEST_CONFIG_ERROR,
+ TEST_ASSERT_ERROR,
+ TEST_CUSTOM_ERROR,
+ TEST_INTERNAL_ERROR,
+};
+
+enum test_peer_attr_type {
+ PEER_AT_AF_FLAG = 0,
+ PEER_AT_AF_FILTER = 1,
+ PEER_AT_AF_CUSTOM = 2,
+ PEER_AT_GLOBAL_FLAG = 3,
+ PEER_AT_GLOBAL_CUSTOM = 4
+};
+
+struct test {
+ enum test_state state;
+ char *desc;
+ char *error;
+ struct list *log;
+
+ struct vty *vty;
+ struct bgp *bgp;
+ struct peer *peer;
+ struct peer_group *group;
+
+ struct {
+ bool use_ibgp;
+ bool use_iface_peer;
+ } o;
+};
+
+struct test_config {
+ int local_asn;
+ int peer_asn;
+ const char *peer_address;
+ const char *peer_interface;
+ const char *peer_group;
+};
+
+struct test_peer_family {
+ afi_t afi;
+ safi_t safi;
+};
+
+struct test_peer_attr {
+ const char *cmd;
+ const char *peer_cmd;
+ const char *group_cmd;
+
+ enum test_peer_attr_type type;
+ union {
+ uint64_t flag;
+ struct {
+ uint64_t flag;
+ size_t direct;
+ } filter;
+ } u;
+ struct {
+ bool invert_peer;
+ bool invert_group;
+ bool use_ibgp;
+ bool use_iface_peer;
+ bool skip_xfer_cases;
+ } o;
+
+ afi_t afi;
+ safi_t safi;
+ struct test_peer_family families[AFI_MAX * SAFI_MAX];
+
+ void (*handlers[TEST_HANDLER_MAX])(struct test *test,
+ struct test_peer_attr *pa,
+ struct peer *peer,
+ struct peer *group, bool peer_set,
+ bool group_set);
+};
+
+static struct test_config cfg = {
+ .local_asn = 100,
+ .peer_asn = 200,
+ .peer_address = "1.1.1.1",
+ .peer_interface = "IP-TEST",
+ .peer_group = "PG-TEST",
+};
+
+static struct test_peer_family test_default_families[] = {
+ {.afi = AFI_IP, .safi = SAFI_UNICAST},
+ {.afi = AFI_IP, .safi = SAFI_MULTICAST},
+ {.afi = AFI_IP6, .safi = SAFI_UNICAST},
+ {.afi = AFI_IP6, .safi = SAFI_MULTICAST},
+};
+
+TEST_ATTR_HANDLER_DECL(advertisement_interval, v_routeadv, 10, 20);
+TEST_STR_ATTR_HANDLER_DECL(password, password, "FRR-Peer", "FRR-Group");
+TEST_ATTR_HANDLER_DECL(local_as, change_local_as, 1, 2);
+TEST_ATTR_HANDLER_DECL(timers_1, keepalive, 10, 20);
+TEST_ATTR_HANDLER_DECL(timers_2, holdtime, 30, 60);
+TEST_ATTR_HANDLER_DECL(addpath_types, addpath_type[pa->afi][pa->safi],
+ BGP_ADDPATH_ALL, BGP_ADDPATH_BEST_PER_AS);
+TEST_SU_ATTR_HANDLER_DECL(update_source_su, update_source, "255.255.255.1",
+ "255.255.255.2");
+TEST_STR_ATTR_HANDLER_DECL(update_source_if, update_if, "IF-PEER", "IF-GROUP");
+
+TEST_ATTR_HANDLER_DECL(allowas_in, allowas_in[pa->afi][pa->safi], 1, 2);
+TEST_STR_ATTR_HANDLER_DECL(default_originate_route_map,
+ default_rmap[pa->afi][pa->safi].name, "RM-PEER",
+ "RM-GROUP");
+TEST_STR_ATTR_HANDLER_DECL(
+ distribute_list,
+ filter[pa->afi][pa->safi].dlist[pa->u.filter.direct].name, "DL-PEER",
+ "DL-GROUP");
+TEST_STR_ATTR_HANDLER_DECL(
+ filter_list, filter[pa->afi][pa->safi].aslist[pa->u.filter.direct].name,
+ "FL-PEER", "FL-GROUP");
+TEST_ATTR_HANDLER_DECL(maximum_prefix, pmax[pa->afi][pa->safi], 10, 20);
+TEST_ATTR_HANDLER_DECL(maximum_prefix_threshold,
+ pmax_threshold[pa->afi][pa->safi], 1, 2);
+TEST_ATTR_HANDLER_DECL(maximum_prefix_restart, pmax_restart[pa->afi][pa->safi],
+ 100, 200);
+TEST_STR_ATTR_HANDLER_DECL(
+ prefix_list, filter[pa->afi][pa->safi].plist[pa->u.filter.direct].name,
+ "PL-PEER", "PL-GROUP");
+TEST_STR_ATTR_HANDLER_DECL(
+ route_map, filter[pa->afi][pa->safi].map[pa->u.filter.direct].name,
+ "RM-PEER", "RM-GROUP");
+TEST_STR_ATTR_HANDLER_DECL(unsuppress_map, filter[pa->afi][pa->safi].usmap.name,
+ "UM-PEER", "UM-GROUP");
+TEST_ATTR_HANDLER_DECL(weight, weight[pa->afi][pa->safi], 100, 200);
+
+/* clang-format off */
+static struct test_peer_attr test_peer_attrs[] = {
+ /* Peer Attributes */
+ {
+ .cmd = "advertisement-interval",
+ .peer_cmd = "advertisement-interval 10",
+ .group_cmd = "advertisement-interval 20",
+ .u.flag = PEER_FLAG_ROUTEADV,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .handlers[0] = TEST_HANDLER(advertisement_interval),
+ },
+ {
+ .cmd = "capability dynamic",
+ .u.flag = PEER_FLAG_DYNAMIC_CAPABILITY,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "capability extended-nexthop",
+ .u.flag = PEER_FLAG_CAPABILITY_ENHE,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "capability extended-nexthop",
+ .u.flag = PEER_FLAG_CAPABILITY_ENHE,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .o.invert_peer = true,
+ .o.use_iface_peer = true,
+ },
+ {
+ .cmd = "capability software-version",
+ .u.flag = PEER_FLAG_CAPABILITY_SOFT_VERSION,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "capability software-version",
+ .u.flag = PEER_FLAG_CAPABILITY_SOFT_VERSION,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .o.invert_peer = true,
+ .o.use_iface_peer = true,
+ },
+ {
+ .cmd = "description",
+ .peer_cmd = "description FRR Peer",
+ .group_cmd = "description FRR Group",
+ .type = PEER_AT_GLOBAL_CUSTOM,
+ },
+ {
+ .cmd = "disable-connected-check",
+ .u.flag = PEER_FLAG_DISABLE_CONNECTED_CHECK,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "dont-capability-negotiate",
+ .u.flag = PEER_FLAG_DONT_CAPABILITY,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "enforce-first-as",
+ .u.flag = PEER_FLAG_ENFORCE_FIRST_AS,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "local-as",
+ .peer_cmd = "local-as 1",
+ .group_cmd = "local-as 2",
+ .u.flag = PEER_FLAG_LOCAL_AS,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .handlers[0] = TEST_HANDLER(local_as),
+ },
+ {
+ .cmd = "local-as 1 no-prepend",
+ .u.flag = PEER_FLAG_LOCAL_AS | PEER_FLAG_LOCAL_AS_NO_PREPEND,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "local-as 1 no-prepend replace-as",
+ .u.flag = PEER_FLAG_LOCAL_AS | PEER_FLAG_LOCAL_AS_REPLACE_AS,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "override-capability",
+ .u.flag = PEER_FLAG_OVERRIDE_CAPABILITY,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "passive",
+ .u.flag = PEER_FLAG_PASSIVE,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "password",
+ .peer_cmd = "password FRR-Peer",
+ .group_cmd = "password FRR-Group",
+ .u.flag = PEER_FLAG_PASSWORD,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .handlers[0] = TEST_HANDLER(password),
+ },
+ {
+ .cmd = "shutdown",
+ .u.flag = PEER_FLAG_SHUTDOWN,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "strict-capability-match",
+ .u.flag = PEER_FLAG_STRICT_CAP_MATCH,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "timers",
+ .peer_cmd = "timers 10 30",
+ .group_cmd = "timers 20 60",
+ .u.flag = PEER_FLAG_TIMER,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .handlers[0] = TEST_HANDLER(timers_1),
+ .handlers[1] = TEST_HANDLER(timers_2),
+ },
+ {
+ .cmd = "timers connect",
+ .peer_cmd = "timers connect 10",
+ .group_cmd = "timers connect 20",
+ .u.flag = PEER_FLAG_TIMER_CONNECT,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "update-source",
+ .peer_cmd = "update-source 255.255.255.1",
+ .group_cmd = "update-source 255.255.255.2",
+ .u.flag = PEER_FLAG_UPDATE_SOURCE,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .handlers[0] = TEST_HANDLER(update_source_su),
+ },
+ {
+ .cmd = "update-source",
+ .peer_cmd = "update-source IF-PEER",
+ .group_cmd = "update-source IF-GROUP",
+ .u.flag = PEER_FLAG_UPDATE_SOURCE,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .handlers[0] = TEST_HANDLER(update_source_if),
+ },
+
+ /* Address Family Attributes */
+ {
+ .cmd = "addpath",
+ .peer_cmd = "addpath-tx-all-paths",
+ .group_cmd = "addpath-tx-bestpath-per-AS",
+ .type = PEER_AT_AF_CUSTOM,
+ .handlers[0] = TEST_HANDLER(addpath_types),
+ },
+ {
+ .cmd = "allowas-in",
+ .peer_cmd = "allowas-in 1",
+ .group_cmd = "allowas-in 2",
+ .u.flag = PEER_FLAG_ALLOWAS_IN,
+ .handlers[0] = TEST_HANDLER(allowas_in),
+ },
+ {
+ .cmd = "allowas-in origin",
+ .u.flag = PEER_FLAG_ALLOWAS_IN_ORIGIN,
+ },
+ {
+ .cmd = "as-override",
+ .u.flag = PEER_FLAG_AS_OVERRIDE,
+ },
+ {
+ .cmd = "attribute-unchanged as-path",
+ .u.flag = PEER_FLAG_AS_PATH_UNCHANGED,
+ },
+ {
+ .cmd = "attribute-unchanged next-hop",
+ .u.flag = PEER_FLAG_NEXTHOP_UNCHANGED,
+ },
+ {
+ .cmd = "attribute-unchanged med",
+ .u.flag = PEER_FLAG_MED_UNCHANGED,
+ },
+ {
+ .cmd = "attribute-unchanged as-path next-hop",
+ .u.flag = PEER_FLAG_AS_PATH_UNCHANGED
+ | PEER_FLAG_NEXTHOP_UNCHANGED,
+ },
+ {
+ .cmd = "attribute-unchanged as-path med",
+ .u.flag = PEER_FLAG_AS_PATH_UNCHANGED
+ | PEER_FLAG_MED_UNCHANGED,
+ },
+ {
+ .cmd = "attribute-unchanged as-path next-hop med",
+ .u.flag = PEER_FLAG_AS_PATH_UNCHANGED
+ | PEER_FLAG_NEXTHOP_UNCHANGED
+ | PEER_FLAG_MED_UNCHANGED,
+ },
+ {
+ .cmd = "capability orf prefix-list send",
+ .u.flag = PEER_FLAG_ORF_PREFIX_SM,
+ },
+ {
+ .cmd = "capability orf prefix-list receive",
+ .u.flag = PEER_FLAG_ORF_PREFIX_RM,
+ },
+ {
+ .cmd = "capability orf prefix-list both",
+ .u.flag = PEER_FLAG_ORF_PREFIX_SM | PEER_FLAG_ORF_PREFIX_RM,
+ },
+ {
+ .cmd = "default-originate",
+ .u.flag = PEER_FLAG_DEFAULT_ORIGINATE,
+ },
+ {
+ .cmd = "default-originate route-map",
+ .peer_cmd = "default-originate route-map RM-PEER",
+ .group_cmd = "default-originate route-map RM-GROUP",
+ .u.flag = PEER_FLAG_DEFAULT_ORIGINATE,
+ .handlers[0] = TEST_HANDLER(default_originate_route_map),
+ },
+ {
+ .cmd = "distribute-list",
+ .peer_cmd = "distribute-list DL-PEER in",
+ .group_cmd = "distribute-list DL-GROUP in",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_DISTRIBUTE_LIST,
+ .u.filter.direct = FILTER_IN,
+ .handlers[0] = TEST_HANDLER(distribute_list),
+ },
+ {
+ .cmd = "distribute-list",
+ .peer_cmd = "distribute-list DL-PEER out",
+ .group_cmd = "distribute-list DL-GROUP out",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_DISTRIBUTE_LIST,
+ .u.filter.direct = FILTER_OUT,
+ .handlers[0] = TEST_HANDLER(distribute_list),
+ },
+ {
+ .cmd = "filter-list",
+ .peer_cmd = "filter-list FL-PEER in",
+ .group_cmd = "filter-list FL-GROUP in",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_FILTER_LIST,
+ .u.filter.direct = FILTER_IN,
+ .handlers[0] = TEST_HANDLER(filter_list),
+ },
+ {
+ .cmd = "filter-list",
+ .peer_cmd = "filter-list FL-PEER out",
+ .group_cmd = "filter-list FL-GROUP out",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_FILTER_LIST,
+ .u.filter.direct = FILTER_OUT,
+ .handlers[0] = TEST_HANDLER(filter_list),
+ },
+ {
+ .cmd = "maximum-prefix",
+ .peer_cmd = "maximum-prefix 10",
+ .group_cmd = "maximum-prefix 20",
+ .u.flag = PEER_FLAG_MAX_PREFIX,
+ .handlers[0] = TEST_HANDLER(maximum_prefix),
+ },
+ {
+ .cmd = "maximum-prefix",
+ .peer_cmd = "maximum-prefix 10 restart 100",
+ .group_cmd = "maximum-prefix 20 restart 200",
+ .u.flag = PEER_FLAG_MAX_PREFIX,
+ .handlers[0] = TEST_HANDLER(maximum_prefix),
+ .handlers[1] = TEST_HANDLER(maximum_prefix_restart),
+ },
+ {
+ .cmd = "maximum-prefix",
+ .peer_cmd = "maximum-prefix 10 1 restart 100",
+ .group_cmd = "maximum-prefix 20 2 restart 200",
+ .u.flag = PEER_FLAG_MAX_PREFIX,
+ .handlers[0] = TEST_HANDLER(maximum_prefix),
+ .handlers[1] = TEST_HANDLER(maximum_prefix_threshold),
+ .handlers[2] = TEST_HANDLER(maximum_prefix_restart),
+ },
+ {
+ .cmd = "maximum-prefix",
+ .peer_cmd = "maximum-prefix 10 warning-only",
+ .group_cmd = "maximum-prefix 20 warning-only",
+ .u.flag = PEER_FLAG_MAX_PREFIX | PEER_FLAG_MAX_PREFIX_WARNING,
+ .handlers[0] = TEST_HANDLER(maximum_prefix),
+ },
+ {
+ .cmd = "maximum-prefix",
+ .peer_cmd = "maximum-prefix 10 1 warning-only",
+ .group_cmd = "maximum-prefix 20 2 warning-only",
+ .u.flag = PEER_FLAG_MAX_PREFIX | PEER_FLAG_MAX_PREFIX_WARNING,
+ .handlers[0] = TEST_HANDLER(maximum_prefix),
+ .handlers[1] = TEST_HANDLER(maximum_prefix_threshold),
+ },
+ {
+ .cmd = "next-hop-self",
+ .u.flag = PEER_FLAG_NEXTHOP_SELF,
+ },
+ {
+ .cmd = "next-hop-self force",
+ .u.flag = PEER_FLAG_FORCE_NEXTHOP_SELF,
+ },
+ {
+ .cmd = "prefix-list",
+ .peer_cmd = "prefix-list PL-PEER in",
+ .group_cmd = "prefix-list PL-GROUP in",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_PREFIX_LIST,
+ .u.filter.direct = FILTER_IN,
+ .handlers[0] = TEST_HANDLER(prefix_list),
+ },
+ {
+ .cmd = "prefix-list",
+ .peer_cmd = "prefix-list PL-PEER out",
+ .group_cmd = "prefix-list PL-GROUP out",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_PREFIX_LIST,
+ .u.filter.direct = FILTER_OUT,
+ .handlers[0] = TEST_HANDLER(prefix_list),
+ },
+ {
+ .cmd = "remove-private-AS",
+ .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS,
+ },
+ {
+ .cmd = "remove-private-AS all",
+ .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS
+ | PEER_FLAG_REMOVE_PRIVATE_AS_ALL,
+ },
+ {
+ .cmd = "remove-private-AS replace-AS",
+ .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS
+ | PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE,
+ },
+ {
+ .cmd = "remove-private-AS all replace-AS",
+ .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE,
+ },
+ {
+ .cmd = "route-map",
+ .peer_cmd = "route-map RM-PEER in",
+ .group_cmd = "route-map RM-GROUP in",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_ROUTE_MAP,
+ .u.filter.direct = FILTER_IN,
+ .handlers[0] = TEST_HANDLER(route_map),
+ },
+ {
+ .cmd = "route-map",
+ .peer_cmd = "route-map RM-PEER out",
+ .group_cmd = "route-map RM-GROUP out",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_ROUTE_MAP,
+ .u.filter.direct = FILTER_OUT,
+ .handlers[0] = TEST_HANDLER(route_map),
+ },
+ {
+ .cmd = "route-reflector-client",
+ .u.flag = PEER_FLAG_REFLECTOR_CLIENT,
+ .o.use_ibgp = true,
+ .o.skip_xfer_cases = true,
+ },
+ {
+ .cmd = "route-server-client",
+ .u.flag = PEER_FLAG_RSERVER_CLIENT,
+ },
+ {
+ .cmd = "send-community",
+ .u.flag = PEER_FLAG_SEND_COMMUNITY,
+ .o.invert_peer = true,
+ .o.invert_group = true,
+ },
+ {
+ .cmd = "send-community extended",
+ .u.flag = PEER_FLAG_SEND_EXT_COMMUNITY,
+ .o.invert_peer = true,
+ .o.invert_group = true,
+ },
+ {
+ .cmd = "send-community large",
+ .u.flag = PEER_FLAG_SEND_LARGE_COMMUNITY,
+ .o.invert_peer = true,
+ .o.invert_group = true,
+ },
+ {
+ .cmd = "soft-reconfiguration inbound",
+ .u.flag = PEER_FLAG_SOFT_RECONFIG,
+ },
+ {
+ .cmd = "unsuppress-map",
+ .peer_cmd = "unsuppress-map UM-PEER",
+ .group_cmd = "unsuppress-map UM-GROUP",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_UNSUPPRESS_MAP,
+ .u.filter.direct = 0,
+ .handlers[0] = TEST_HANDLER(unsuppress_map),
+ },
+ {
+ .cmd = "weight",
+ .peer_cmd = "weight 100",
+ .group_cmd = "weight 200",
+ .u.flag = PEER_FLAG_WEIGHT,
+ .handlers[0] = TEST_HANDLER(weight),
+ },
+ {
+ .cmd = "accept-own",
+ .peer_cmd = "accept-own",
+ .group_cmd = "accept-own",
+ .families[0] = {.afi = AFI_IP, .safi = SAFI_MPLS_VPN},
+ .families[1] = {.afi = AFI_IP6, .safi = SAFI_MPLS_VPN},
+ .u.flag = PEER_FLAG_ACCEPT_OWN,
+ },
+ {NULL}
+};
+/* clang-format on */
+
+static const char *str_from_afi(afi_t afi)
+{
+ switch (afi) {
+ case AFI_IP:
+ return "ipv4";
+ case AFI_IP6:
+ return "ipv6";
+ case AFI_L2VPN:
+ return "l2vpn";
+ case AFI_MAX:
+ case AFI_UNSPEC:
+ return "bad-value";
+ }
+
+ assert(!"Reached end of function we should never reach");
+}
+
+static const char *str_from_attr_type(enum test_peer_attr_type at)
+{
+ switch (at) {
+ case PEER_AT_GLOBAL_FLAG:
+ return "peer-flag";
+ case PEER_AT_AF_FLAG:
+ return "af-flag";
+ case PEER_AT_AF_FILTER:
+ return "af-filter";
+ case PEER_AT_GLOBAL_CUSTOM:
+ case PEER_AT_AF_CUSTOM:
+ return "custom";
+ default:
+ return NULL;
+ }
+}
+
+static bool is_attr_type_global(enum test_peer_attr_type at)
+{
+ return at == PEER_AT_GLOBAL_FLAG || at == PEER_AT_GLOBAL_CUSTOM;
+}
+
+PRINTFRR(2, 3)
+static void test_log(struct test *test, const char *fmt, ...)
+{
+ va_list ap;
+
+ /* Skip logging if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Store formatted log message. */
+ va_start(ap, fmt);
+ listnode_add(test->log, vasprintfrr(MTYPE_TMP, fmt, ap));
+ va_end(ap);
+}
+
+PRINTFRR(2, 3)
+static void test_execute(struct test *test, const char *fmt, ...)
+{
+ int ret;
+ char *cmd;
+ va_list ap;
+ vector vline;
+
+ /* Skip execution if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Format command string with variadic arguments. */
+ va_start(ap, fmt);
+ cmd = vasprintfrr(MTYPE_TMP, fmt, ap);
+ va_end(ap);
+ if (!cmd) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP, "could not format command string [%s]", fmt);
+ return;
+ }
+
+ /* Tokenize formatted command string. */
+ vline = cmd_make_strvec(cmd);
+ if (vline == NULL) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP,
+ "tokenizing command string [%s] returned empty result",
+ cmd);
+ XFREE(MTYPE_TMP, cmd);
+
+ return;
+ }
+
+ /* Execute command (non-strict). */
+ ret = cmd_execute_command(vline, test->vty, NULL, 0);
+ if (ret != CMD_SUCCESS) {
+ test->state = TEST_COMMAND_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP,
+ "execution of command [%s] has failed with code [%d]",
+ cmd, ret);
+ }
+
+ /* Free memory. */
+ cmd_free_strvec(vline);
+ XFREE(MTYPE_TMP, cmd);
+}
+
+PRINTFRR(2, 0)
+static void test_config(struct test *test, const char *fmt, bool invert,
+ va_list ap)
+{
+ char *matcher;
+ char *config;
+ bool matched;
+ va_list apc;
+
+ /* Skip execution if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Format matcher string with variadic arguments. */
+ va_copy(apc, ap);
+ matcher = vasprintfrr(MTYPE_TMP, fmt, apc);
+ va_end(apc);
+ if (!matcher) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP, "could not format matcher string [%s]", fmt);
+ return;
+ }
+
+ /* Fetch BGP configuration into buffer. */
+ bgp_config_write(test->vty);
+ config = buffer_getstr(test->vty->obuf);
+ buffer_reset(test->vty->obuf);
+
+ /* Match config against matcher. */
+ matched = !!strstr(config, matcher);
+ if (!matched && !invert) {
+ test->state = TEST_CONFIG_ERROR;
+ test->error = asprintfrr(MTYPE_TMP,
+ "expected config [%s] to be present",
+ matcher);
+ } else if (matched && invert) {
+ test->state = TEST_CONFIG_ERROR;
+ test->error = asprintfrr(MTYPE_TMP,
+ "expected config [%s] to be absent",
+ matcher);
+ }
+
+ /* Free memory and return. */
+ XFREE(MTYPE_TMP, matcher);
+ XFREE(MTYPE_TMP, config);
+}
+
+PRINTFRR(2, 3)
+static void test_config_present(struct test *test, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ test_config(test, fmt, false, ap);
+ va_end(ap);
+}
+
+PRINTFRR(2, 3)
+static void test_config_absent(struct test *test, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ test_config(test, fmt, true, ap);
+ va_end(ap);
+}
+
+static void test_initialize(struct test *test)
+{
+ union sockunion su;
+
+ /* Skip execution if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Log message about (re)-initialization */
+ test_log(test, "prepare: %sinitialize bgp test environment",
+ test->bgp ? "re-" : "");
+
+ /* Attempt gracefully to purge previous BGP configuration. */
+ test_execute(test, "no router bgp");
+ test->state = TEST_SUCCESS;
+
+ /* Initialize BGP test environment. */
+ test_execute(test, "router bgp %d", cfg.local_asn);
+ test_execute(test, "no bgp default ipv4-unicast");
+ test_execute(test, "neighbor %s peer-group", cfg.peer_group);
+ if (test->o.use_iface_peer) {
+ test_execute(test, "neighbor %s interface", cfg.peer_interface);
+ test_execute(test, "neighbor %s remote-as %d",
+ cfg.peer_interface,
+ test->o.use_ibgp ? cfg.local_asn : cfg.peer_asn);
+ } else {
+ test_execute(test, "neighbor %s remote-as %d", cfg.peer_address,
+ test->o.use_ibgp ? cfg.local_asn : cfg.peer_asn);
+ }
+
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Fetch default BGP instance. */
+ test->bgp = bgp_get_default();
+ if (!test->bgp) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP, "could not retrieve default bgp instance");
+ return;
+ }
+
+ /* Fetch peer instance. */
+ if (test->o.use_iface_peer) {
+ test->peer =
+ peer_lookup_by_conf_if(test->bgp, cfg.peer_interface);
+ } else {
+ str2sockunion(cfg.peer_address, &su);
+ test->peer = peer_lookup(test->bgp, &su);
+ }
+ if (!test->peer) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP,
+ "could not retrieve instance of bgp peer [%s]",
+ cfg.peer_address);
+ return;
+ }
+
+ /* Fetch peer-group instance. */
+ test->group = peer_group_lookup(test->bgp, cfg.peer_group);
+ if (!test->group) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP,
+ "could not retrieve instance of bgp peer-group [%s]",
+ cfg.peer_group);
+ return;
+ }
+}
+
+static struct test *test_new(const char *desc, bool use_ibgp,
+ bool use_iface_peer)
+{
+ struct test *test;
+
+ test = XCALLOC(MTYPE_TMP, sizeof(struct test));
+ test->state = TEST_SUCCESS;
+ test->desc = XSTRDUP(MTYPE_TMP, desc);
+ test->log = list_new();
+ test->o.use_ibgp = use_ibgp;
+ test->o.use_iface_peer = use_iface_peer;
+
+ test->vty = vty_new();
+ test->vty->type = VTY_TERM;
+ test->vty->node = CONFIG_NODE;
+
+ test_initialize(test);
+
+ return test;
+};
+
+static void test_finish(struct test *test)
+{
+ char *msg;
+ struct listnode *node, *nnode;
+
+ /* Print test output header. */
+ printf("%s [test] %s\n",
+ (test->state == TEST_SUCCESS) ? OUT_SYMBOL_OK : OUT_SYMBOL_NOK,
+ test->desc);
+
+ /* Print test log messages. */
+ for (ALL_LIST_ELEMENTS(test->log, node, nnode, msg)) {
+ printf("%s %s\n", OUT_SYMBOL_INFO, msg);
+ XFREE(MTYPE_TMP, msg);
+ }
+
+ /* Print test error message if available. */
+ if (test->state != TEST_SUCCESS && test->error)
+ printf("%s error: %s\n", OUT_SYMBOL_INFO, test->error);
+
+ /* Print machine-readable result of test. */
+ printf("%s\n", test->state == TEST_SUCCESS ? "OK" : "failed");
+
+ /* Cleanup allocated memory. */
+ if (test->vty) {
+ vty_close(test->vty);
+ test->vty = NULL;
+ }
+ if (test->log)
+ list_delete(&test->log);
+ if (test->desc)
+ XFREE(MTYPE_TMP, test->desc);
+ if (test->error)
+ XFREE(MTYPE_TMP, test->error);
+ XFREE(MTYPE_TMP, test);
+}
+
+static void test_peer_flags(struct test *test, struct test_peer_attr *pa,
+ struct peer *peer, bool exp_val, bool exp_ovrd)
+{
+ bool exp_inv, cur_val, cur_ovrd, cur_inv;
+
+ /* Skip execution if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Detect if flag is meant to be inverted. */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ exp_inv = pa->o.invert_group;
+ else
+ exp_inv = pa->o.invert_peer;
+
+ /* Flip expected value if flag is inverted. */
+ exp_val ^= exp_inv;
+
+ /* Fetch current state of value, override and invert flags. */
+ if (pa->type == PEER_AT_GLOBAL_FLAG) {
+ cur_val = !!CHECK_FLAG(peer->flags, pa->u.flag);
+ cur_ovrd = !!CHECK_FLAG(peer->flags_override, pa->u.flag);
+ cur_inv = !!CHECK_FLAG(peer->flags_invert, pa->u.flag);
+ } else /* if (pa->type == PEER_AT_AF_FLAG) */ {
+ cur_val = !!CHECK_FLAG(peer->af_flags[pa->afi][pa->safi],
+ pa->u.flag);
+ cur_ovrd = !!CHECK_FLAG(
+ peer->af_flags_override[pa->afi][pa->safi], pa->u.flag);
+ cur_inv = !!CHECK_FLAG(peer->af_flags_invert[pa->afi][pa->safi],
+ pa->u.flag);
+ }
+
+ /* Assert expected flag states. */
+ TEST_ASSERT_EQ(test, cur_val, exp_val);
+ TEST_ASSERT_EQ(test, cur_ovrd, exp_ovrd);
+ TEST_ASSERT_EQ(test, cur_inv, exp_inv);
+}
+
+static void test_af_filter(struct test *test, struct test_peer_attr *pa,
+ struct peer *peer, bool exp_state, bool exp_ovrd)
+{
+ bool cur_ovrd;
+ struct bgp_filter *filter;
+
+ /* Skip execution if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Fetch and assert current state of override flag. */
+ cur_ovrd = !!CHECK_FLAG(
+ peer->filter_override[pa->afi][pa->safi][pa->u.filter.direct],
+ pa->u.filter.flag);
+
+ TEST_ASSERT_EQ(test, cur_ovrd, exp_ovrd);
+
+ /* Assert that map/list matches expected state (set/unset). */
+ filter = &peer->filter[pa->afi][pa->safi];
+
+ switch (pa->u.filter.flag) {
+ case PEER_FT_DISTRIBUTE_LIST:
+ TEST_ASSERT_EQ(test,
+ !!(filter->dlist[pa->u.filter.direct].name),
+ exp_state);
+ break;
+ case PEER_FT_FILTER_LIST:
+ TEST_ASSERT_EQ(test,
+ !!(filter->aslist[pa->u.filter.direct].name),
+ exp_state);
+ break;
+ case PEER_FT_PREFIX_LIST:
+ TEST_ASSERT_EQ(test,
+ !!(filter->plist[pa->u.filter.direct].name),
+ exp_state);
+ break;
+ case PEER_FT_ROUTE_MAP:
+ TEST_ASSERT_EQ(test, !!(filter->map[pa->u.filter.direct].name),
+ exp_state);
+ break;
+ case PEER_FT_UNSUPPRESS_MAP:
+ TEST_ASSERT_EQ(test, !!(filter->usmap.name), exp_state);
+ break;
+ }
+}
+
+static void test_custom(struct test *test, struct test_peer_attr *pa,
+ struct peer *peer, struct peer *group, bool peer_set,
+ bool group_set)
+{
+ int i;
+ char *handler_error;
+
+ for (i = 0; i < TEST_HANDLER_MAX; i++) {
+ /* Skip execution if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Skip further execution if handler is undefined. */
+ if (!pa->handlers[i])
+ return;
+
+ /* Execute custom handler. */
+ pa->handlers[i](test, pa, peer, group, peer_set, group_set);
+ if (test->state != TEST_SUCCESS) {
+ test->state = TEST_CUSTOM_ERROR;
+ handler_error = test->error;
+ test->error = asprintfrr(MTYPE_TMP,
+ "custom handler failed: %s",
+ handler_error);
+ XFREE(MTYPE_TMP, handler_error);
+ }
+ }
+}
+
+
+static void test_process(struct test *test, struct test_peer_attr *pa,
+ struct peer *peer, struct peer *group, bool peer_set,
+ bool group_set)
+{
+ switch (pa->type) {
+ case PEER_AT_GLOBAL_FLAG:
+ case PEER_AT_AF_FLAG:
+ test_peer_flags(
+ test, pa, peer,
+ peer_set || (peer_group_active(peer) && group_set),
+ peer_set);
+ test_peer_flags(test, pa, group, group_set, false);
+ break;
+
+ case PEER_AT_AF_FILTER:
+ test_af_filter(
+ test, pa, peer,
+ peer_set || (peer_group_active(peer) && group_set),
+ peer_set);
+ test_af_filter(test, pa, group, group_set, false);
+ break;
+
+ case PEER_AT_GLOBAL_CUSTOM:
+ case PEER_AT_AF_CUSTOM:
+ /*
+ * Do nothing here - a custom handler can be executed, but this
+ * is not required. This will allow defining peer attributes
+ * which shall not be checked for flag/filter/other internal
+ * states.
+ */
+ break;
+
+ default:
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP, "invalid attribute type: %d", pa->type);
+ break;
+ }
+
+ /* Attempt to call a custom handler if set for further processing. */
+ test_custom(test, pa, peer, group, peer_set, group_set);
+}
+
+static void test_peer_attr(struct test *test, struct test_peer_attr *pa)
+{
+ int tc = 1;
+ const char *type;
+ const char *ecp = pa->o.invert_peer ? "no " : "";
+ const char *dcp = pa->o.invert_peer ? "" : "no ";
+ const char *ecg = pa->o.invert_group ? "no " : "";
+ const char *dcg = pa->o.invert_group ? "" : "no ";
+ const char *peer_cmd = pa->peer_cmd ?: pa->cmd;
+ const char *group_cmd = pa->group_cmd ?: pa->cmd;
+ struct peer *p = test->peer;
+ struct peer_group *g = test->group;
+
+ /* Determine type and if test is address-family relevant */
+ type = str_from_attr_type(pa->type);
+ if (!type) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP, "invalid attribute type: %d", pa->type);
+ return;
+ }
+
+ /*
+ * =====================================================================
+ * Test Case Suite 1: Config persistence after adding peer to group
+ *
+ * Example: If a peer attribute has value [1] and a group attribute has
+ * value [2], the peer attribute value should be persisted when the peer
+ * gets added to the peer-group.
+ *
+ * This test suite is meant to test the group2peer functions which can
+ * be found inside bgpd/bgpd.c, which are related to initial peer-group
+ * inheritance.
+ * =====================================================================
+ */
+
+ /* Test Preparation: Switch and activate address-family. */
+ if (!is_attr_type_global(pa->type)) {
+ test_log(test, "prepare: switch address-family to [%s]",
+ get_afi_safi_str(pa->afi, pa->safi, false));
+ test_execute(test, "address-family %s %s",
+ str_from_afi(pa->afi), safi2str(pa->safi));
+ test_execute(test, "neighbor %s activate", g->name);
+ test_execute(test, "neighbor %s activate", p->host);
+ }
+
+ /* Skip peer-group to peer transfer test cases if requested. */
+ if (pa->o.skip_xfer_cases && test->state == TEST_SUCCESS)
+ test->state = TEST_SKIPPING;
+
+ /* Test Case: Set flag on BGP peer. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, peer_cmd,
+ p->host);
+ test_execute(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+
+ /* Test Case: Set flag on BGP peer-group. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, group_cmd,
+ g->name);
+ test_execute(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, true, true);
+
+ /* Test Case: Add BGP peer to peer-group. */
+ test_log(test, "case %02d: add peer [%s] to group [%s]", tc++, p->host,
+ g->name);
+ test_execute(test, "neighbor %s peer-group %s", p->host, g->name);
+ test_config_present(test, "neighbor %s %speer-group %s", p->host,
+ p->conf_if ? "interface " : "", g->name);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, true, true);
+
+ /* Test Case: Unset flag on BGP peer-group. */
+ test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type,
+ group_cmd, g->name);
+ test_execute(test, "%sneighbor %s %s", dcg, g->name, group_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+
+ /*
+ * =====================================================================
+ * Test Case Suite 2: Config inheritance after adding peer to group
+ *
+ * Example: If a peer attribute has not been set and a group attribute
+ * has a value of [2], the group attribute should be inherited to the
+ * peer without flagging the newly set value as overridden.
+ *
+ * This test suite is meant to test the group2peer functions which can
+ * be found inside bgpd/bgpd.c, which are related to initial peer-group
+ * inheritance.
+ * =====================================================================
+ */
+
+ /* Test Preparation: Re-initialize test environment. */
+ test_initialize(test);
+ p = test->peer;
+ g = test->group;
+
+ /* Test Preparation: Switch and activate address-family. */
+ if (!is_attr_type_global(pa->type)) {
+ test_log(test, "prepare: switch address-family to [%s]",
+ get_afi_safi_str(pa->afi, pa->safi, false));
+ test_execute(test, "address-family %s %s",
+ str_from_afi(pa->afi), safi2str(pa->safi));
+ test_execute(test, "neighbor %s activate", g->name);
+ test_execute(test, "neighbor %s activate", p->host);
+ }
+
+ /* Test Case: Set flag on BGP peer-group. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, group_cmd,
+ g->name);
+ test_execute(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_config_absent(test, "neighbor %s %s", p->host, pa->cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, false, true);
+
+ /* Test Case: Add BGP peer to peer-group. */
+ test_log(test, "case %02d: add peer [%s] to group [%s]", tc++, p->host,
+ g->name);
+ test_execute(test, "neighbor %s peer-group %s", p->host, g->name);
+ test_config_present(test, "neighbor %s %speer-group %s", p->host,
+ p->conf_if ? "interface " : "", g->name);
+ test_config_absent(test, "neighbor %s %s", p->host, pa->cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, false, true);
+
+ /* Stop skipping test cases if previously enabled. */
+ if (pa->o.skip_xfer_cases && test->state == TEST_SKIPPING)
+ test->state = TEST_SUCCESS;
+
+ /*
+ * =====================================================================
+ * Test Case Suite 3: Miscellaneous flag checks
+ *
+ * This test suite does not focus on initial peer-group inheritance and
+ * instead executes various different commands to set/unset attributes
+ * on both peer- and group-level. These checks should always be executed
+ * and must pass.
+ * =====================================================================
+ */
+
+ /* Test Preparation: Re-initialize test environment. */
+ test_initialize(test);
+ p = test->peer;
+ g = test->group;
+
+ /* Test Preparation: Switch and activate address-family. */
+ if (!is_attr_type_global(pa->type)) {
+ test_log(test, "prepare: switch address-family to [%s]",
+ get_afi_safi_str(pa->afi, pa->safi, false));
+ test_execute(test, "address-family %s %s",
+ str_from_afi(pa->afi), safi2str(pa->safi));
+ test_execute(test, "neighbor %s activate", g->name);
+ test_execute(test, "neighbor %s activate", p->host);
+ }
+
+ /* Test Case: Set flag on BGP peer. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, peer_cmd,
+ p->host);
+ test_execute(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+
+ /* Test Case: Add BGP peer to peer-group. */
+ test_log(test, "case %02d: add peer [%s] to group [%s]", tc++, p->host,
+ g->name);
+ test_execute(test, "neighbor %s peer-group %s", p->host, g->name);
+ test_config_present(test, "neighbor %s %speer-group %s", p->host,
+ p->conf_if ? "interface " : "", g->name);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+
+ /* Test Case: Re-add BGP peer to peer-group. */
+ test_log(test, "case %02d: re-add peer [%s] to group [%s]", tc++,
+ p->host, g->name);
+ test_execute(test, "neighbor %s peer-group %s", p->host, g->name);
+ test_config_present(test, "neighbor %s %speer-group %s", p->host,
+ p->conf_if ? "interface " : "", g->name);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+
+ /* Test Case: Set flag on BGP peer-group. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, group_cmd,
+ g->name);
+ test_execute(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, true, true);
+
+ /* Test Case: Unset flag on BGP peer-group. */
+ test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type,
+ group_cmd, g->name);
+ test_execute(test, "%sneighbor %s %s", dcg, g->name, group_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+
+ /* Test Case: Set flag on BGP peer-group. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, group_cmd,
+ g->name);
+ test_execute(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, true, true);
+
+ /* Test Case: Re-set flag on BGP peer. */
+ test_log(test, "case %02d: re-set %s [%s] on [%s]", tc++, type,
+ peer_cmd, p->host);
+ test_execute(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, true, true);
+
+ /* Test Case: Unset flag on BGP peer. */
+ test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type, peer_cmd,
+ p->host);
+ test_execute(test, "%sneighbor %s %s", dcp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", p->host, pa->cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, false, true);
+
+ /* Test Case: Unset flag on BGP peer-group. */
+ test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type,
+ group_cmd, g->name);
+ test_execute(test, "%sneighbor %s %s", dcg, g->name, group_cmd);
+ test_config_absent(test, "neighbor %s %s", p->host, pa->cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, false, false);
+
+ /* Test Case: Set flag on BGP peer. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, peer_cmd,
+ p->host);
+ test_execute(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+}
+
+static void bgp_startup(void)
+{
+ cmd_init(1);
+ zlog_aux_init("NONE: ", LOG_DEBUG);
+ zprivs_preinit(&bgpd_privs);
+ zprivs_init(&bgpd_privs);
+
+ master = event_master_create(NULL);
+ nb_init(master, NULL, 0, false);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
+ bgp_option_set(BGP_OPT_NO_LISTEN);
+ vrf_init(NULL, NULL, NULL, NULL);
+ frr_pthread_init();
+ bgp_init(0);
+ bgp_pthreads_run();
+}
+
+static void bgp_shutdown(void)
+{
+ struct bgp *bgp;
+ struct listnode *node, *nnode;
+
+ bgp_terminate();
+ bgp_close();
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ bgp_delete(bgp);
+ bgp_dump_finish();
+ bgp_route_finish();
+ bgp_route_map_terminate();
+ bgp_attr_finish();
+ bgp_pthreads_finish();
+ access_list_add_hook(NULL);
+ access_list_delete_hook(NULL);
+ access_list_reset();
+ as_list_add_hook(NULL);
+ as_list_delete_hook(NULL);
+ bgp_filter_reset();
+ prefix_list_add_hook(NULL);
+ prefix_list_delete_hook(NULL);
+ prefix_list_reset();
+ community_list_terminate(bgp_clist);
+ vrf_terminate();
+#ifdef ENABLE_BGP_VNC
+ vnc_zebra_destroy();
+#endif
+ bgp_zebra_destroy();
+
+ bf_free(bm->rd_idspace);
+ list_delete(&bm->bgp);
+ memset(bm, 0, sizeof(*bm));
+
+ vty_terminate();
+ cmd_terminate();
+ nb_terminate();
+ yang_terminate();
+ zprivs_terminate(&bgpd_privs);
+ event_master_free(master);
+ master = NULL;
+}
+
+int main(void)
+{
+ int i, ii;
+ struct list *pa_list;
+ struct test_peer_attr *pa, *pac;
+ struct listnode *node, *nnode;
+
+ bgp_startup();
+
+ pa_list = list_new();
+ i = 0;
+ while (test_peer_attrs[i].cmd) {
+ pa = &test_peer_attrs[i++];
+
+ /* Just copy the peer attribute structure for global flags. */
+ if (is_attr_type_global(pa->type)) {
+ pac = XMALLOC(MTYPE_TMP, sizeof(struct test_peer_attr));
+ memcpy(pac, pa, sizeof(struct test_peer_attr));
+ listnode_add(pa_list, pac);
+ continue;
+ }
+
+ /* Fallback to default families if not specified. */
+ if (!pa->families[0].afi && !pa->families[0].safi)
+ memcpy(&pa->families, test_default_families,
+ sizeof(test_default_families));
+
+ /* Add peer attribute definition for each address family. */
+ ii = 0;
+ while (pa->families[ii].afi && pa->families[ii].safi) {
+ pac = XMALLOC(MTYPE_TMP, sizeof(struct test_peer_attr));
+ memcpy(pac, pa, sizeof(struct test_peer_attr));
+
+ pac->afi = pa->families[ii].afi;
+ pac->safi = pa->families[ii].safi;
+ listnode_add(pa_list, pac);
+
+ ii++;
+ }
+ }
+
+ for (ALL_LIST_ELEMENTS(pa_list, node, nnode, pa)) {
+ char *desc;
+ struct test *test;
+
+ /* Build test description string. */
+ if (pa->afi && pa->safi)
+ desc = asprintfrr(MTYPE_TMP, "peer\\%s-%s\\%s",
+ str_from_afi(pa->afi),
+ safi2str(pa->safi), pa->cmd);
+ else
+ desc = asprintfrr(MTYPE_TMP, "peer\\%s", pa->cmd);
+
+ /* Initialize new test instance. */
+ test = test_new(desc, pa->o.use_ibgp, pa->o.use_iface_peer);
+ XFREE(MTYPE_TMP, desc);
+
+ /* Execute tests and finish test instance. */
+ test_peer_attr(test, pa);
+ test_finish(test);
+
+ /* Print empty line as spacer. */
+ printf("\n");
+
+ /* Free memory used for peer-attr declaration. */
+ XFREE(MTYPE_TMP, pa);
+ }
+
+ list_delete(&pa_list);
+ bgp_shutdown();
+
+ return 0;
+}
diff --git a/tests/bgpd/test_peer_attr.py b/tests/bgpd/test_peer_attr.py
new file mode 100644
index 0000000..eb57618
--- /dev/null
+++ b/tests/bgpd/test_peer_attr.py
@@ -0,0 +1,200 @@
+import frrtest
+
+
+class TestFlag(frrtest.TestMultiOut):
+ program = "./test_peer_attr"
+
+
+# List of tests can be generated by executing:
+# $> ./test_peer_attr 2>&1 | sed -n 's/\\/\\\\/g; s/\S\+ \[test\] \(.\+\)/TestFlag.okfail(\x27\1\x27)/pg'
+#
+TestFlag.okfail("peer\\advertisement-interval")
+TestFlag.okfail("peer\\capability dynamic")
+TestFlag.okfail("peer\\capability extended-nexthop")
+# TestFlag.okfail('peer\\capability extended-nexthop')
+TestFlag.okfail("peer\\description")
+TestFlag.okfail("peer\\disable-connected-check")
+TestFlag.okfail("peer\\dont-capability-negotiate")
+TestFlag.okfail("peer\\enforce-first-as")
+TestFlag.okfail("peer\\local-as")
+TestFlag.okfail("peer\\local-as 1 no-prepend")
+TestFlag.okfail("peer\\local-as 1 no-prepend replace-as")
+TestFlag.okfail("peer\\override-capability")
+TestFlag.okfail("peer\\passive")
+TestFlag.okfail("peer\\password")
+TestFlag.okfail("peer\\shutdown")
+TestFlag.okfail("peer\\strict-capability-match")
+TestFlag.okfail("peer\\timers")
+TestFlag.okfail("peer\\timers connect")
+TestFlag.okfail("peer\\update-source")
+TestFlag.okfail("peer\\update-source")
+TestFlag.okfail("peer\\ipv4-unicast\\addpath")
+TestFlag.okfail("peer\\ipv4-multicast\\addpath")
+TestFlag.okfail("peer\\ipv6-unicast\\addpath")
+TestFlag.okfail("peer\\ipv6-multicast\\addpath")
+TestFlag.okfail("peer\\ipv4-unicast\\allowas-in")
+TestFlag.okfail("peer\\ipv4-multicast\\allowas-in")
+TestFlag.okfail("peer\\ipv6-unicast\\allowas-in")
+TestFlag.okfail("peer\\ipv6-multicast\\allowas-in")
+TestFlag.okfail("peer\\ipv4-unicast\\allowas-in origin")
+TestFlag.okfail("peer\\ipv4-multicast\\allowas-in origin")
+TestFlag.okfail("peer\\ipv6-unicast\\allowas-in origin")
+TestFlag.okfail("peer\\ipv6-multicast\\allowas-in origin")
+TestFlag.okfail("peer\\ipv4-unicast\\as-override")
+TestFlag.okfail("peer\\ipv4-multicast\\as-override")
+TestFlag.okfail("peer\\ipv6-unicast\\as-override")
+TestFlag.okfail("peer\\ipv6-multicast\\as-override")
+TestFlag.okfail("peer\\ipv4-unicast\\attribute-unchanged as-path")
+TestFlag.okfail("peer\\ipv4-multicast\\attribute-unchanged as-path")
+TestFlag.okfail("peer\\ipv6-unicast\\attribute-unchanged as-path")
+TestFlag.okfail("peer\\ipv6-multicast\\attribute-unchanged as-path")
+TestFlag.okfail("peer\\ipv4-unicast\\attribute-unchanged next-hop")
+TestFlag.okfail("peer\\ipv4-multicast\\attribute-unchanged next-hop")
+TestFlag.okfail("peer\\ipv6-unicast\\attribute-unchanged next-hop")
+TestFlag.okfail("peer\\ipv6-multicast\\attribute-unchanged next-hop")
+TestFlag.okfail("peer\\ipv4-unicast\\attribute-unchanged med")
+TestFlag.okfail("peer\\ipv4-multicast\\attribute-unchanged med")
+TestFlag.okfail("peer\\ipv6-unicast\\attribute-unchanged med")
+TestFlag.okfail("peer\\ipv6-multicast\\attribute-unchanged med")
+TestFlag.okfail("peer\\ipv4-unicast\\attribute-unchanged as-path next-hop")
+TestFlag.okfail("peer\\ipv4-multicast\\attribute-unchanged as-path next-hop")
+TestFlag.okfail("peer\\ipv6-unicast\\attribute-unchanged as-path next-hop")
+TestFlag.okfail("peer\\ipv6-multicast\\attribute-unchanged as-path next-hop")
+TestFlag.okfail("peer\\ipv4-unicast\\attribute-unchanged as-path med")
+TestFlag.okfail("peer\\ipv4-multicast\\attribute-unchanged as-path med")
+TestFlag.okfail("peer\\ipv6-unicast\\attribute-unchanged as-path med")
+TestFlag.okfail("peer\\ipv6-multicast\\attribute-unchanged as-path med")
+TestFlag.okfail("peer\\ipv4-unicast\\attribute-unchanged as-path next-hop med")
+TestFlag.okfail("peer\\ipv4-multicast\\attribute-unchanged as-path next-hop med")
+TestFlag.okfail("peer\\ipv6-unicast\\attribute-unchanged as-path next-hop med")
+TestFlag.okfail("peer\\ipv6-multicast\\attribute-unchanged as-path next-hop med")
+TestFlag.okfail("peer\\ipv4-unicast\\capability orf prefix-list send")
+TestFlag.okfail("peer\\ipv4-multicast\\capability orf prefix-list send")
+TestFlag.okfail("peer\\ipv6-unicast\\capability orf prefix-list send")
+TestFlag.okfail("peer\\ipv6-multicast\\capability orf prefix-list send")
+TestFlag.okfail("peer\\ipv4-unicast\\capability orf prefix-list receive")
+TestFlag.okfail("peer\\ipv4-multicast\\capability orf prefix-list receive")
+TestFlag.okfail("peer\\ipv6-unicast\\capability orf prefix-list receive")
+TestFlag.okfail("peer\\ipv6-multicast\\capability orf prefix-list receive")
+TestFlag.okfail("peer\\ipv4-unicast\\capability orf prefix-list both")
+TestFlag.okfail("peer\\ipv4-multicast\\capability orf prefix-list both")
+TestFlag.okfail("peer\\ipv6-unicast\\capability orf prefix-list both")
+TestFlag.okfail("peer\\ipv6-multicast\\capability orf prefix-list both")
+TestFlag.okfail("peer\\ipv4-unicast\\default-originate")
+TestFlag.okfail("peer\\ipv4-multicast\\default-originate")
+TestFlag.okfail("peer\\ipv6-unicast\\default-originate")
+TestFlag.okfail("peer\\ipv6-multicast\\default-originate")
+TestFlag.okfail("peer\\ipv4-unicast\\default-originate route-map")
+TestFlag.okfail("peer\\ipv4-multicast\\default-originate route-map")
+TestFlag.okfail("peer\\ipv6-unicast\\default-originate route-map")
+TestFlag.okfail("peer\\ipv6-multicast\\default-originate route-map")
+TestFlag.okfail("peer\\ipv4-unicast\\distribute-list")
+TestFlag.okfail("peer\\ipv4-multicast\\distribute-list")
+TestFlag.okfail("peer\\ipv6-unicast\\distribute-list")
+TestFlag.okfail("peer\\ipv6-multicast\\distribute-list")
+TestFlag.okfail("peer\\ipv4-unicast\\distribute-list")
+TestFlag.okfail("peer\\ipv4-multicast\\distribute-list")
+TestFlag.okfail("peer\\ipv6-unicast\\distribute-list")
+TestFlag.okfail("peer\\ipv6-multicast\\distribute-list")
+TestFlag.okfail("peer\\ipv4-unicast\\filter-list")
+TestFlag.okfail("peer\\ipv4-multicast\\filter-list")
+TestFlag.okfail("peer\\ipv6-unicast\\filter-list")
+TestFlag.okfail("peer\\ipv6-multicast\\filter-list")
+TestFlag.okfail("peer\\ipv4-unicast\\filter-list")
+TestFlag.okfail("peer\\ipv4-multicast\\filter-list")
+TestFlag.okfail("peer\\ipv6-unicast\\filter-list")
+TestFlag.okfail("peer\\ipv6-multicast\\filter-list")
+TestFlag.okfail("peer\\ipv4-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-unicast\\next-hop-self")
+TestFlag.okfail("peer\\ipv4-multicast\\next-hop-self")
+TestFlag.okfail("peer\\ipv6-unicast\\next-hop-self")
+TestFlag.okfail("peer\\ipv6-multicast\\next-hop-self")
+TestFlag.okfail("peer\\ipv4-unicast\\next-hop-self force")
+TestFlag.okfail("peer\\ipv4-multicast\\next-hop-self force")
+TestFlag.okfail("peer\\ipv6-unicast\\next-hop-self force")
+TestFlag.okfail("peer\\ipv6-multicast\\next-hop-self force")
+TestFlag.okfail("peer\\ipv4-unicast\\prefix-list")
+TestFlag.okfail("peer\\ipv4-multicast\\prefix-list")
+TestFlag.okfail("peer\\ipv6-unicast\\prefix-list")
+TestFlag.okfail("peer\\ipv6-multicast\\prefix-list")
+TestFlag.okfail("peer\\ipv4-unicast\\prefix-list")
+TestFlag.okfail("peer\\ipv4-multicast\\prefix-list")
+TestFlag.okfail("peer\\ipv6-unicast\\prefix-list")
+TestFlag.okfail("peer\\ipv6-multicast\\prefix-list")
+TestFlag.okfail("peer\\ipv4-unicast\\remove-private-AS")
+TestFlag.okfail("peer\\ipv4-multicast\\remove-private-AS")
+TestFlag.okfail("peer\\ipv6-unicast\\remove-private-AS")
+TestFlag.okfail("peer\\ipv6-multicast\\remove-private-AS")
+TestFlag.okfail("peer\\ipv4-unicast\\remove-private-AS all")
+TestFlag.okfail("peer\\ipv4-multicast\\remove-private-AS all")
+TestFlag.okfail("peer\\ipv6-unicast\\remove-private-AS all")
+TestFlag.okfail("peer\\ipv6-multicast\\remove-private-AS all")
+TestFlag.okfail("peer\\ipv4-unicast\\remove-private-AS replace-AS")
+TestFlag.okfail("peer\\ipv4-multicast\\remove-private-AS replace-AS")
+TestFlag.okfail("peer\\ipv6-unicast\\remove-private-AS replace-AS")
+TestFlag.okfail("peer\\ipv6-multicast\\remove-private-AS replace-AS")
+TestFlag.okfail("peer\\ipv4-unicast\\remove-private-AS all replace-AS")
+TestFlag.okfail("peer\\ipv4-multicast\\remove-private-AS all replace-AS")
+TestFlag.okfail("peer\\ipv6-unicast\\remove-private-AS all replace-AS")
+TestFlag.okfail("peer\\ipv6-multicast\\remove-private-AS all replace-AS")
+TestFlag.okfail("peer\\ipv4-unicast\\route-map")
+TestFlag.okfail("peer\\ipv4-multicast\\route-map")
+TestFlag.okfail("peer\\ipv6-unicast\\route-map")
+TestFlag.okfail("peer\\ipv6-multicast\\route-map")
+TestFlag.okfail("peer\\ipv4-unicast\\route-map")
+TestFlag.okfail("peer\\ipv4-multicast\\route-map")
+TestFlag.okfail("peer\\ipv6-unicast\\route-map")
+TestFlag.okfail("peer\\ipv6-multicast\\route-map")
+TestFlag.okfail("peer\\ipv4-unicast\\route-reflector-client")
+TestFlag.okfail("peer\\ipv4-multicast\\route-reflector-client")
+TestFlag.okfail("peer\\ipv6-unicast\\route-reflector-client")
+TestFlag.okfail("peer\\ipv6-multicast\\route-reflector-client")
+TestFlag.okfail("peer\\ipv4-unicast\\route-server-client")
+TestFlag.okfail("peer\\ipv4-multicast\\route-server-client")
+TestFlag.okfail("peer\\ipv6-unicast\\route-server-client")
+TestFlag.okfail("peer\\ipv6-multicast\\route-server-client")
+TestFlag.okfail("peer\\ipv4-unicast\\send-community")
+TestFlag.okfail("peer\\ipv4-multicast\\send-community")
+TestFlag.okfail("peer\\ipv6-unicast\\send-community")
+TestFlag.okfail("peer\\ipv6-multicast\\send-community")
+TestFlag.okfail("peer\\ipv4-unicast\\send-community extended")
+TestFlag.okfail("peer\\ipv4-multicast\\send-community extended")
+TestFlag.okfail("peer\\ipv6-unicast\\send-community extended")
+TestFlag.okfail("peer\\ipv6-multicast\\send-community extended")
+TestFlag.okfail("peer\\ipv4-unicast\\send-community large")
+TestFlag.okfail("peer\\ipv4-multicast\\send-community large")
+TestFlag.okfail("peer\\ipv6-unicast\\send-community large")
+TestFlag.okfail("peer\\ipv6-multicast\\send-community large")
+TestFlag.okfail("peer\\ipv4-unicast\\soft-reconfiguration inbound")
+TestFlag.okfail("peer\\ipv4-multicast\\soft-reconfiguration inbound")
+TestFlag.okfail("peer\\ipv6-unicast\\soft-reconfiguration inbound")
+TestFlag.okfail("peer\\ipv6-multicast\\soft-reconfiguration inbound")
+TestFlag.okfail("peer\\ipv4-unicast\\unsuppress-map")
+TestFlag.okfail("peer\\ipv4-multicast\\unsuppress-map")
+TestFlag.okfail("peer\\ipv6-unicast\\unsuppress-map")
+TestFlag.okfail("peer\\ipv6-multicast\\unsuppress-map")
+TestFlag.okfail("peer\\ipv4-unicast\\weight")
+TestFlag.okfail("peer\\ipv4-multicast\\weight")
+TestFlag.okfail("peer\\ipv6-unicast\\weight")
+TestFlag.okfail("peer\\ipv6-multicast\\weight")
+TestFlag.okfail("peer\\ipv4-vpn\\accept-own")
+TestFlag.okfail("peer\\ipv6-vpn\\accept-own")
diff --git a/tests/helpers/c/main.c b/tests/helpers/c/main.c
new file mode 100644
index 0000000..8af53a2
--- /dev/null
+++ b/tests/helpers/c/main.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "frrevent.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "lib_vty.h"
+
+extern void test_init(void);
+
+struct event_loop *master;
+
+struct option longopts[] = {{"daemon", no_argument, NULL, 'd'},
+ {"config_file", required_argument, NULL, 'f'},
+ {"help", no_argument, NULL, 'h'},
+ {"vty_addr", required_argument, NULL, 'A'},
+ {"vty_port", required_argument, NULL, 'P'},
+ {"version", no_argument, NULL, 'v'},
+ {0}};
+
+DEFUN (daemon_exit,
+ daemon_exit_cmd,
+ "daemon-exit",
+ "Make the daemon exit\n")
+{
+ exit(0);
+}
+
+static int timer_count;
+static void test_timer(struct event *thread)
+{
+ int *count = EVENT_ARG(thread);
+
+ printf("run %d of timer\n", (*count)++);
+ event_add_timer(master, test_timer, count, 5, NULL);
+}
+
+static void test_timer_init(void)
+{
+ event_add_timer(master, test_timer, &timer_count, 10, NULL);
+}
+
+static void test_vty_init(void)
+{
+ install_element(VIEW_NODE, &daemon_exit_cmd);
+}
+
+/* Help information display. */
+static void usage(char *progname, int status)
+{
+ if (status != 0)
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ progname);
+ else {
+ printf("Usage : %s [OPTION...]\n\
+Daemon which does 'slow' things.\n\n\
+-d, --daemon Runs in daemon mode\n\
+-f, --config_file Set configuration file name\n\
+-A, --vty_addr Set vty's bind address\n\
+-P, --vty_port Set vty's port number\n\
+-v, --version Print program version\n\
+-h, --help Display this help and exit\n\
+\n\
+Report bugs to %s\n",
+ progname, FRR_BUG_ADDRESS);
+ }
+ exit(status);
+}
+
+
+/* main routine. */
+int main(int argc, char **argv)
+{
+ char *p;
+ char *vty_addr = NULL;
+ int vty_port = 4000;
+ int daemon_mode = 0;
+ char *progname;
+ struct event thread;
+ char *config_file = NULL;
+
+ /* Set umask before anything for security */
+ umask(0027);
+
+ /* get program name */
+ progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
+
+ /* master init. */
+ master = event_master_create(NULL);
+
+ while (1) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "dhf:A:P:v", longopts, 0);
+
+ if (opt == EOF)
+ break;
+
+ switch (opt) {
+ case 0:
+ break;
+ case 'f':
+ config_file = optarg;
+ break;
+ case 'd':
+ daemon_mode = 1;
+ break;
+ case 'A':
+ vty_addr = optarg;
+ break;
+ case 'P':
+ /* Deal with atoi() returning 0 on failure */
+ if (strcmp(optarg, "0") == 0) {
+ vty_port = 0;
+ break;
+ }
+ vty_port = atoi(optarg);
+ vty_port = (vty_port ? vty_port : 4000);
+ break;
+ case 'v':
+ print_version(progname);
+ exit(0);
+ break;
+ case 'h':
+ usage(progname, 0);
+ break;
+ default:
+ usage(progname, 1);
+ break;
+ }
+ }
+
+ /* Library inits. */
+ cmd_init(1);
+ vty_init(master, false);
+ lib_cmd_init();
+ nb_init(master, NULL, 0, false);
+
+ /* OSPF vty inits. */
+ test_vty_init();
+
+ /* Change to the daemon program. */
+ if (daemon_mode && daemon(0, 0) < 0) {
+ fprintf(stderr, "daemon failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ /* Create VTY socket */
+ vty_serv_start(vty_addr, vty_port, "/tmp/.heavy.sock");
+
+ /* Configuration file read*/
+ if (!config_file)
+ usage(progname, 1);
+ vty_read_config(NULL, config_file, NULL);
+
+ test_timer_init();
+
+ test_init();
+
+ /* Fetch next active thread. */
+ while (event_fetch(master, &thread))
+ event_call(&thread);
+
+ /* Not reached. */
+ exit(0);
+}
diff --git a/tests/helpers/c/prng.c b/tests/helpers/c/prng.c
new file mode 100644
index 0000000..612c433
--- /dev/null
+++ b/tests/helpers/c/prng.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Very simple prng to allow for randomized tests with reproducable
+ * results.
+ *
+ * Copyright (C) 2012 by Open Source Routing.
+ * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2017 Christian Franke
+ *
+ * This file is part of Quagga
+ */
+
+#include <zebra.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "prng.h"
+
+struct prng {
+ uint64_t state;
+};
+
+struct prng *prng_new(unsigned long long seed)
+{
+ struct prng *rv = calloc(sizeof(*rv), 1);
+ assert(rv);
+
+ rv->state = seed;
+
+ return rv;
+}
+
+/*
+ * This implementation has originally been provided to musl libc by
+ * Szabolcs Nagy <nsz at port70 dot net> in 2013 under the terms of
+ * the MIT license.
+ * It is a simple LCG which D.E. Knuth attributes to C.E. Haynes in
+ * TAOCP Vol2 3.3.4
+ */
+int prng_rand(struct prng *prng)
+{
+ prng->state = 6364136223846793005ULL * prng->state + 1;
+ return prng->state >> 33;
+}
+
+const char *prng_fuzz(struct prng *prng, const char *string,
+ const char *charset, unsigned int operations)
+{
+ static char buf[256];
+ unsigned int charset_len;
+ unsigned int i;
+ unsigned int offset;
+ unsigned int op;
+ unsigned int character;
+
+ assert(strlen(string) < sizeof(buf));
+
+ strncpy(buf, string, sizeof(buf));
+ charset_len = strlen(charset);
+
+ for (i = 0; i < operations; i++) {
+ offset = prng_rand(prng) % strlen(buf);
+ op = prng_rand(prng) % 3;
+
+ switch (op) {
+ case 0:
+ /* replace */
+ character = prng_rand(prng) % charset_len;
+ buf[offset] = charset[character];
+ break;
+ case 1:
+ /* remove */
+ memmove(buf + offset, buf + offset + 1,
+ strlen(buf) - offset);
+ break;
+ case 2:
+ /* insert */
+ assert(strlen(buf) + 1 < sizeof(buf));
+
+ memmove(buf + offset + 1, buf + offset,
+ strlen(buf) + 1 - offset);
+ character = prng_rand(prng) % charset_len;
+ buf[offset] = charset[character];
+ break;
+ }
+ }
+ return buf;
+}
+
+void prng_free(struct prng *prng)
+{
+ free(prng);
+}
diff --git a/tests/helpers/c/prng.h b/tests/helpers/c/prng.h
new file mode 100644
index 0000000..6b10bde
--- /dev/null
+++ b/tests/helpers/c/prng.h
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Very simple prng to allow for randomized tests with reproducable
+ * results.
+ *
+ * Copyright (C) 2012 by Open Source Routing.
+ * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of Quagga
+ */
+#ifndef _PRNG_H
+#define _PRNG_H
+
+struct prng;
+
+struct prng *prng_new(unsigned long long seed);
+int prng_rand(struct prng *);
+const char *prng_fuzz(struct prng *, const char *string, const char *charset,
+ unsigned int operations);
+void prng_free(struct prng *);
+
+#endif
diff --git a/tests/helpers/c/tests.h b/tests/helpers/c/tests.h
new file mode 100644
index 0000000..40f17cc
--- /dev/null
+++ b/tests/helpers/c/tests.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Test wrappers common header file
+ *
+ * Copyright (C) 2015 by David Lamparter,
+ * for Open Source Routing./ NetDEF, Inc.
+ *
+ * This file is part of Quagga
+ */
+
+#ifndef _QUAGGA_TESTS_H
+#define _QUAGGA_TESTS_H
+
+extern void test_init(void);
+extern void test_init_cmd(void);
+
+#endif /* _QUAGGA_TESTS_H */
diff --git a/tests/helpers/python/frrsix.py b/tests/helpers/python/frrsix.py
new file mode 100644
index 0000000..8af9647
--- /dev/null
+++ b/tests/helpers/python/frrsix.py
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: MIT
+#
+# Copyright (c) 2010-2017 Benjamin Peterson
+#
+
+#
+# This code is taken from the six python2 to python3 compatibility module
+#
+
+import sys
+
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+
+
+def add_metaclass(metaclass):
+ """Class decorator for creating a class with a metaclass."""
+
+ def wrapper(cls):
+ orig_vars = cls.__dict__.copy()
+ slots = orig_vars.get("__slots__")
+ if slots is not None:
+ if isinstance(slots, str):
+ slots = [slots]
+ for slots_var in slots:
+ orig_vars.pop(slots_var)
+ orig_vars.pop("__dict__", None)
+ orig_vars.pop("__weakref__", None)
+ return metaclass(cls.__name__, cls.__bases__, orig_vars)
+
+ return wrapper
+
+
+if PY3:
+ import builtins
+
+ exec_ = getattr(builtins, "exec")
+
+ def reraise(tp, value, tb=None):
+ try:
+ if value is None:
+ value = tp()
+ if value.__traceback__ is not tb:
+ raise value.with_traceback(tb)
+ raise value
+ finally:
+ value = None
+ tb = None
+
+
+else:
+
+ def exec_(_code_, _globs_=None, _locs_=None):
+ """Execute code in a namespace."""
+ if _globs_ is None:
+ frame = sys._getframe(1)
+ _globs_ = frame.f_globals
+ if _locs_ is None:
+ _locs_ = frame.f_locals
+ del frame
+ elif _locs_ is None:
+ _locs_ = _globs_
+ exec("""exec _code_ in _globs_, _locs_""")
+
+ exec_(
+ """def reraise(tp, value, tb=None):
+ try:
+ raise tp, value, tb
+ finally:
+ tb = None
+"""
+ )
diff --git a/tests/helpers/python/frrtest.py b/tests/helpers/python/frrtest.py
new file mode 100644
index 0000000..3faa2a6
--- /dev/null
+++ b/tests/helpers/python/frrtest.py
@@ -0,0 +1,211 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Test helpers for FRR
+#
+# Copyright (C) 2017 by David Lamparter & Christian Franke,
+# Open Source Routing / NetDEF Inc.
+#
+# This file is part of FRRouting (FRR)
+#
+
+import subprocess
+import sys
+import re
+import inspect
+import os
+import difflib
+
+import frrsix
+
+#
+# These are the gritty internals of the TestMultiOut implementation.
+# See below for the definition of actual TestMultiOut tests.
+#
+
+srcbase = os.path.abspath(inspect.getsourcefile(frrsix))
+for i in range(0, 3):
+ srcbase = os.path.dirname(srcbase)
+
+
+def binpath(srcpath):
+ return os.path.relpath(os.path.abspath(srcpath), srcbase)
+
+
+class MultiTestFailure(Exception):
+ pass
+
+
+class MetaTestMultiOut(type):
+ def __getattr__(cls, name):
+ if name.startswith("_"):
+ raise AttributeError
+
+ internal_name = "_{}".format(name)
+ if internal_name not in dir(cls):
+ raise AttributeError
+
+ def registrar(*args, **kwargs):
+ cls._add_test(getattr(cls, internal_name), *args, **kwargs)
+
+ return registrar
+
+
+@frrsix.add_metaclass(MetaTestMultiOut)
+class _TestMultiOut(object):
+ def _run_tests(self):
+ if "tests_run" in dir(self.__class__) and self.tests_run:
+ return
+ self.__class__.tests_run = True
+ basedir = os.path.dirname(inspect.getsourcefile(type(self)))
+ program = os.path.join(basedir, self.program)
+ proc = subprocess.Popen([binpath(program)], stdout=subprocess.PIPE)
+ self.output, _ = proc.communicate("")
+ self.exitcode = proc.wait()
+
+ self.__class__.testresults = {}
+ for test in self.tests:
+ try:
+ test(self)
+ except MultiTestFailure:
+ self.testresults[test] = sys.exc_info()
+ else:
+ self.testresults[test] = None
+
+ def _exit_cleanly(self):
+ if self.exitcode != 0:
+ raise MultiTestFailure("Program did not terminate with exit code 0")
+
+ @classmethod
+ def _add_test(cls, method, *args, **kwargs):
+ if "tests" not in dir(cls):
+ setattr(cls, "tests", [])
+ if method is not cls._exit_cleanly:
+ cls._add_test(cls._exit_cleanly)
+
+ def matchfunction(self):
+ method(self, *args, **kwargs)
+
+ cls.tests.append(matchfunction)
+
+ def testfunction(self):
+ self._run_tests()
+ result = self.testresults[matchfunction]
+ if result is not None:
+ frrsix.reraise(*result)
+
+ testname = re.sub(r"[^A-Za-z0-9]", "_", "%r%r" % (args, kwargs))
+ testname = re.sub(r"__*", "_", testname)
+ testname = testname.strip("_")
+ if not testname:
+ testname = method.__name__.strip("_")
+ if "test_%s" % testname in dir(cls):
+ index = 2
+ while "test_%s_%d" % (testname, index) in dir(cls):
+ index += 1
+ testname = "%s_%d" % (testname, index)
+ setattr(cls, "test_%s" % testname, testfunction)
+
+
+#
+# This class houses the actual TestMultiOut tests types.
+# If you want to add a new test type, you probably do it here.
+#
+# Say you want to add a test type called foobarlicious. Then define
+# a function _foobarlicious here that takes self and the test arguments
+# when called. That function should check the output in self.output
+# to see whether it matches the expectation of foobarlicious with the
+# given arguments and should then adjust self.output according to how
+# much output it consumed.
+# If the output doesn't meet the expectations, MultiTestFailure can be
+# raised, however that should only be done after self.output has been
+# modified according to consumed content.
+#
+
+re_okfail = re.compile(r"(?:[3[12]m|^)?(?P<ret>OK|failed)".encode("utf8"), re.MULTILINE)
+
+
+class TestMultiOut(_TestMultiOut):
+ def _onesimple(self, line):
+ if type(line) is str:
+ line = line.encode("utf8")
+ idx = self.output.find(line)
+ if idx != -1:
+ self.output = self.output[idx + len(line) :]
+ else:
+ raise MultiTestFailure("%r could not be found" % line)
+
+ def _okfail(self, line, okfail=re_okfail):
+ self._onesimple(line)
+
+ m = okfail.search(self.output)
+ if m is None:
+ raise MultiTestFailure("OK/fail not found")
+ self.output = self.output[m.end() :]
+
+ if m.group("ret") != "OK".encode("utf8"):
+ raise MultiTestFailure("Test output indicates failure")
+
+
+#
+# This class implements a test comparing the output of a program against
+# an existing reference output
+#
+
+
+class TestRefMismatch(Exception):
+ def __init__(self, _test, outtext, reftext):
+ self.outtext = outtext
+ self.reftext = reftext
+
+ def __str__(self):
+ rv = "Expected output and actual output differ:\n"
+ rv += "\n".join(
+ difflib.unified_diff(
+ self.reftext.splitlines(),
+ self.outtext.splitlines(),
+ "outtext",
+ "reftext",
+ lineterm="",
+ )
+ )
+ return rv
+
+
+class TestExitNonzero(Exception):
+ pass
+
+
+class TestRefOut(object):
+ def test_refout(self):
+ basedir = os.path.dirname(inspect.getsourcefile(type(self)))
+ program = os.path.join(basedir, self.program)
+
+ if getattr(self, "built_refin", False):
+ refin = binpath(program) + ".in"
+ else:
+ refin = program + ".in"
+ if getattr(self, "built_refout", False):
+ refout = binpath(program) + ".refout"
+ else:
+ refout = program + ".refout"
+
+ intext = ""
+ if os.path.exists(refin):
+ with open(refin, "rb") as f:
+ intext = f.read()
+ with open(refout, "rb") as f:
+ reftext = f.read()
+
+ proc = subprocess.Popen(
+ [binpath(program)], stdin=subprocess.PIPE, stdout=subprocess.PIPE
+ )
+ outtext, _ = proc.communicate(intext)
+
+ # Get rid of newline problems (Windows vs Unix Style)
+ outtext_str = outtext.decode("utf8").replace("\r\n", "\n").replace("\r", "\n")
+ reftext_str = reftext.decode("utf8").replace("\r\n", "\n").replace("\r", "\n")
+
+ if outtext_str != reftext_str:
+ raise TestRefMismatch(self, outtext_str, reftext_str)
+ if proc.wait() != 0:
+ raise TestExitNonzero(self)
diff --git a/tests/isisd/.gitignore b/tests/isisd/.gitignore
new file mode 100644
index 0000000..e124221
--- /dev/null
+++ b/tests/isisd/.gitignore
@@ -0,0 +1 @@
+/*_afl/*
diff --git a/tests/isisd/subdir.am b/tests/isisd/subdir.am
new file mode 100644
index 0000000..5adc162
--- /dev/null
+++ b/tests/isisd/subdir.am
@@ -0,0 +1,66 @@
+if !ISISD
+PYTEST_IGNORE += --ignore=isisd/
+endif
+ISISD_TEST_LDADD = isisd/libisis.a $(ALL_TESTS_LDADD)
+noinst_HEADERS += \
+ tests/isisd/test_common.h \
+ # end
+
+
+if ISISD
+check_PROGRAMS += tests/isisd/test_fuzz_isis_tlv
+endif
+tests_isisd_test_fuzz_isis_tlv_CFLAGS = $(TESTS_CFLAGS) -I$(top_builddir)/tests/isisd
+tests_isisd_test_fuzz_isis_tlv_CPPFLAGS = $(TESTS_CPPFLAGS) -I$(top_builddir)/tests/isisd
+tests_isisd_test_fuzz_isis_tlv_LDADD = $(ISISD_TEST_LDADD)
+tests_isisd_test_fuzz_isis_tlv_SOURCES = tests/isisd/test_fuzz_isis_tlv.c tests/isisd/test_common.c
+nodist_tests_isisd_test_fuzz_isis_tlv_SOURCES = tests/isisd/test_fuzz_isis_tlv_tests.h
+EXTRA_DIST += \
+ tests/isisd/test_fuzz_isis_tlv.py \
+ tests/isisd/test_fuzz_isis_tlv_tests.h.gz \
+ # end
+
+tests/isisd/test_fuzz_isis_tlv_tests.h: $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz
+ @$(MKDIR_P) tests/isisd
+ $(AM_V_GEN)gzip -d < $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz > "$@"
+CLEANFILES += tests/isisd/test_fuzz_isis_tlv_tests.h
+
+tests/isisd/tests_isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.$(OBJEXT): \
+ tests/isisd/test_fuzz_isis_tlv_tests.h
+tests/isisd/test_fuzz_isis_tlv-test_fuzz_isis_tlv.$(OBJEXT): \
+ tests/isisd/test_fuzz_isis_tlv_tests.h
+
+
+if ISISD
+check_PROGRAMS += tests/isisd/test_isis_lspdb
+endif
+tests_isisd_test_isis_lspdb_CFLAGS = $(TESTS_CFLAGS)
+tests_isisd_test_isis_lspdb_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_isisd_test_isis_lspdb_LDADD = $(ISISD_TEST_LDADD)
+tests_isisd_test_isis_lspdb_SOURCES = tests/isisd/test_isis_lspdb.c tests/isisd/test_common.c
+EXTRA_DIST += tests/isisd/test_isis_lspdb.py
+
+
+if ISISD
+check_PROGRAMS += tests/isisd/test_isis_spf
+endif
+tests_isisd_test_isis_spf_CFLAGS = $(TESTS_CFLAGS)
+tests_isisd_test_isis_spf_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_isisd_test_isis_spf_LDADD = $(ISISD_TEST_LDADD)
+tests_isisd_test_isis_spf_SOURCES = tests/isisd/test_isis_spf.c tests/isisd/test_common.c tests/isisd/test_topologies.c
+nodist_tests_isisd_test_isis_spf_SOURCES = yang/frr-isisd.yang.c
+EXTRA_DIST += \
+ tests/isisd/test_isis_spf.py \
+ tests/isisd/test_isis_spf.in \
+ tests/isisd/test_isis_spf.refout \
+ # end
+
+
+if ISISD
+check_PROGRAMS += tests/isisd/test_isis_vertex_queue
+endif
+tests_isisd_test_isis_vertex_queue_CFLAGS = $(TESTS_CFLAGS)
+tests_isisd_test_isis_vertex_queue_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_isisd_test_isis_vertex_queue_LDADD = $(ISISD_TEST_LDADD)
+tests_isisd_test_isis_vertex_queue_SOURCES = tests/isisd/test_isis_vertex_queue.c tests/isisd/test_common.c
+EXTRA_DIST += tests/isisd/test_isis_vertex_queue.py
diff --git a/tests/isisd/test_common.c b/tests/isisd/test_common.c
new file mode 100644
index 0000000..e474569
--- /dev/null
+++ b/tests/isisd/test_common.c
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#include <zebra.h>
+
+#include "isisd/isisd.h"
+#include "isisd/isis_dynhn.h"
+#include "isisd/isis_mt.h"
+
+#include "test_common.h"
+
+struct event_loop *master;
+struct zebra_privs_t isisd_privs;
+
+int isis_sock_init(struct isis_circuit *circuit)
+{
+ return 0;
+}
+
+const struct isis_test_node *
+test_topology_find_node(const struct isis_topology *topology,
+ const char *hostname, uint8_t pseudonode_id)
+{
+ for (size_t i = 0; topology->nodes[i].hostname[0]; i++)
+ if (strmatch(hostname, topology->nodes[i].hostname)
+ && pseudonode_id == topology->nodes[i].pseudonode_id)
+ return &topology->nodes[i];
+
+ return NULL;
+}
+
+const struct isis_topology *
+test_topology_find(struct isis_topology *test_topologies, uint16_t number)
+{
+ for (size_t i = 0; test_topologies[i].number; i++)
+ if (test_topologies[i].number == number)
+ return &test_topologies[i];
+
+ return NULL;
+}
+
+static const struct isis_test_node *
+test_find_adjacency(const struct isis_test_node *tnode, const char *hostname)
+{
+ for (size_t i = 0; tnode->adjacencies[i].hostname[0]; i++) {
+ const struct isis_test_adj *tadj;
+
+ tadj = &tnode->adjacencies[i];
+ if (strmatch(hostname, tadj->hostname))
+ return tnode;
+ }
+
+ return NULL;
+}
+
+mpls_label_t test_topology_node_ldp_label(const struct isis_topology *topology,
+ struct in_addr router_id)
+{
+ for (size_t i = 0; topology->nodes[i].hostname[0]; i++) {
+ const struct isis_test_node *tnode = &topology->nodes[i];
+ struct in_addr node_router_id;
+
+ if (!tnode->router_id)
+ continue;
+
+ (void)inet_pton(AF_INET, tnode->router_id, &node_router_id);
+ if (IPV4_ADDR_SAME(&router_id, &node_router_id))
+ return (50000 + (i + 1) * 100);
+ }
+
+ return MPLS_INVALID_LABEL;
+}
+
+static struct isis_lsp *lsp_add(struct lspdb_head *lspdb,
+ struct isis_area *area, int level,
+ const uint8_t *sysid, uint8_t pseudonode_id)
+{
+ struct isis_lsp *lsp;
+ uint8_t lspid[ISIS_SYS_ID_LEN + 2];
+
+ memcpy(lspid, sysid, ISIS_SYS_ID_LEN);
+ LSP_PSEUDO_ID(lspid) = pseudonode_id;
+ LSP_FRAGMENT(lspid) = 0;
+
+ lsp = lsp_new(area, lspid, 6000, 1, 0, 0, NULL, level);
+ lsp->tlvs = isis_alloc_tlvs();
+ lspdb_add(lspdb, lsp);
+
+ return lsp;
+}
+
+static void lsp_add_ip_reach(struct isis_lsp *lsp,
+ const struct isis_test_node *tnode,
+ const char *prefix_str, uint32_t *next_sid_index)
+{
+ struct prefix prefix;
+ struct sr_prefix_cfg pcfg = {};
+ struct sr_prefix_cfg *pcfg_p[SR_ALGORITHM_COUNT] = {NULL};
+
+ if (str2prefix(prefix_str, &prefix) != 1) {
+ zlog_debug("%s: invalid network: %s", __func__, prefix_str);
+ return;
+ }
+
+ if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) {
+ pcfg_p[SR_ALGORITHM_SPF] = &pcfg;
+
+ pcfg.sid = *next_sid_index;
+ *next_sid_index = *next_sid_index + 1;
+ pcfg.sid_type = SR_SID_VALUE_TYPE_INDEX;
+ pcfg.node_sid = true;
+ pcfg.last_hop_behavior = SR_LAST_HOP_BEHAVIOR_PHP;
+ }
+
+ if (prefix.family == AF_INET)
+ isis_tlvs_add_extended_ip_reach(lsp->tlvs,
+ (struct prefix_ipv4 *)&prefix,
+ 10, false, pcfg_p);
+ else
+ isis_tlvs_add_ipv6_reach(lsp->tlvs, ISIS_MT_IPV6_UNICAST,
+ (struct prefix_ipv6 *)&prefix, 10,
+ false, pcfg_p);
+}
+
+static void lsp_add_reach(struct isis_lsp *lsp,
+ const struct isis_test_node *tnode,
+ const uint8_t *ne_id, uint8_t pseudonode_id,
+ uint32_t metric, int family, mpls_label_t *next_label)
+{
+ uint8_t nodeid[ISIS_SYS_ID_LEN + 1];
+ uint16_t mtid;
+ struct isis_ext_subtlvs *ext = NULL;
+
+ memcpy(nodeid, ne_id, ISIS_SYS_ID_LEN);
+ LSP_PSEUDO_ID(nodeid) = pseudonode_id;
+
+ if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) {
+ struct isis_adj_sid *adj_sid;
+
+ adj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*adj_sid));
+ adj_sid->family = family;
+ SET_FLAG(adj_sid->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
+ SET_FLAG(adj_sid->flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG);
+ if (family == AF_INET6)
+ SET_FLAG(adj_sid->flags, EXT_SUBTLV_LINK_ADJ_SID_FFLG);
+ adj_sid->weight = 0;
+ adj_sid->sid = *next_label;
+ *next_label = *next_label + 1;
+
+ ext = isis_alloc_ext_subtlvs();
+ isis_tlvs_add_adj_sid(ext, adj_sid);
+ }
+
+ mtid = (family == AF_INET) ? ISIS_MT_IPV4_UNICAST
+ : ISIS_MT_IPV6_UNICAST;
+
+ isis_tlvs_add_extended_reach(lsp->tlvs, mtid, nodeid, metric, ext);
+}
+
+static void lsp_add_router_capability(struct isis_lsp *lsp,
+ const struct isis_test_node *tnode)
+{
+ struct isis_router_cap *cap;
+
+ if (!tnode->router_id)
+ return;
+
+ cap = isis_tlvs_init_router_capability(lsp->tlvs);
+
+ if (inet_pton(AF_INET, tnode->router_id, &cap->router_id) != 1) {
+ zlog_debug("%s: invalid router-id: %s", __func__,
+ tnode->router_id);
+ return;
+ }
+
+ if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) {
+ cap->srgb.flags =
+ ISIS_SUBTLV_SRGB_FLAG_I | ISIS_SUBTLV_SRGB_FLAG_V;
+ cap->srgb.lower_bound = tnode->srgb.lower_bound
+ ? tnode->srgb.lower_bound
+ : SRGB_DFTL_LOWER_BOUND;
+ cap->srgb.range_size = tnode->srgb.range_size
+ ? tnode->srgb.range_size
+ : SRGB_DFTL_RANGE_SIZE;
+ cap->algo[0] = SR_ALGORITHM_SPF;
+ cap->algo[1] = SR_ALGORITHM_UNSET;
+ }
+
+}
+
+static void lsp_add_mt_router_info(struct isis_lsp *lsp,
+ const struct isis_test_node *tnode)
+{
+ if (tnode->protocols.ipv4)
+ isis_tlvs_add_mt_router_info(lsp->tlvs, ISIS_MT_IPV4_UNICAST, 0,
+ false);
+ if (tnode->protocols.ipv6)
+ isis_tlvs_add_mt_router_info(lsp->tlvs, ISIS_MT_IPV6_UNICAST, 0,
+ false);
+}
+
+static void lsp_add_protocols_supported(struct isis_lsp *lsp,
+ const struct isis_test_node *tnode)
+{
+ struct nlpids nlpids = {};
+
+ if (!tnode->protocols.ipv4 && !tnode->protocols.ipv6)
+ return;
+
+ if (tnode->protocols.ipv4) {
+ nlpids.nlpids[nlpids.count] = NLPID_IP;
+ nlpids.count++;
+ }
+ if (tnode->protocols.ipv6) {
+ nlpids.nlpids[nlpids.count] = NLPID_IPV6;
+ nlpids.count++;
+ }
+ isis_tlvs_set_protocols_supported(lsp->tlvs, &nlpids);
+}
+
+static int topology_load_node_level(const struct isis_topology *topology,
+ const struct isis_test_node *tnode,
+ size_t tnode_index, struct isis_area *area,
+ struct lspdb_head *lspdb, int level)
+{
+ struct isis_lsp *lsp;
+ uint32_t next_sid_index = (tnode_index + 1) * 10;
+ mpls_label_t next_label = 16;
+
+ lsp = lsp_add(lspdb, area, level, tnode->sysid, tnode->pseudonode_id);
+ lsp_add_mt_router_info(lsp, tnode);
+ lsp_add_protocols_supported(lsp, tnode);
+ lsp_add_router_capability(lsp, tnode);
+
+ /* Add IP Reachability Information. */
+ for (size_t i = 0; tnode->networks[i]; i++) {
+ if (i > MAX_NETWORKS) {
+ zlog_debug(
+ "%s: node has too many networks (maximum is %u)",
+ __func__, MAX_NETWORKS);
+ return -1;
+ }
+ lsp_add_ip_reach(lsp, tnode, tnode->networks[i],
+ &next_sid_index);
+ }
+
+ /* Add IS Reachability Information. */
+ for (size_t i = 0; tnode->adjacencies[i].hostname[0]; i++) {
+ const struct isis_test_adj *tadj;
+ const struct isis_test_node *tadj_node;
+
+ if (i > MAX_ADJACENCIES) {
+ zlog_debug(
+ "%s: node has too many adjacencies (maximum is %u)",
+ __func__, MAX_ADJACENCIES);
+ return -1;
+ }
+
+ tadj = &tnode->adjacencies[i];
+ tadj_node = test_topology_find_node(topology, tadj->hostname,
+ tadj->pseudonode_id);
+ if (!tadj_node) {
+ zlog_debug(
+ "%s: node \"%s\" has an adjacency with non-existing node \"%s\"",
+ __func__, tnode->hostname, tadj->hostname);
+ return -1;
+ }
+ if (!test_find_adjacency(tadj_node, tnode->hostname)) {
+ zlog_debug(
+ "%s: node \"%s\" has an one-way adjacency with node \"%s\"",
+ __func__, tnode->hostname, tadj->hostname);
+ return -1;
+ }
+
+ if (tnode->pseudonode_id || tadj_node->pseudonode_id
+ || (tnode->protocols.ipv4 && tadj_node->protocols.ipv4))
+ lsp_add_reach(lsp, tnode, tadj_node->sysid,
+ tadj_node->pseudonode_id, tadj->metric,
+ AF_INET, &next_label);
+ if (tadj_node->pseudonode_id
+ || (tnode->protocols.ipv6 && tadj_node->protocols.ipv6))
+ lsp_add_reach(lsp, tnode, tadj_node->sysid,
+ tadj_node->pseudonode_id, tadj->metric,
+ AF_INET6, &next_label);
+ }
+
+ return 0;
+}
+
+static int topology_load_node(const struct isis_topology *topology,
+ const struct isis_test_node *tnode,
+ size_t tnode_index, struct isis_area *area,
+ struct lspdb_head lspdb[])
+{
+ int ret;
+
+ isis_dynhn_insert(area->isis, tnode->sysid, tnode->hostname,
+ tnode->level);
+
+ for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) {
+ if ((tnode->level & level) == 0)
+ continue;
+
+ ret = topology_load_node_level(topology, tnode, tnode_index,
+ area, &lspdb[level - 1], level);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+int test_topology_load(const struct isis_topology *topology,
+ struct isis_area *area, struct lspdb_head lspdb[])
+{
+ for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++)
+ lsp_db_init(&lspdb[level - 1]);
+
+ for (size_t i = 0; topology->nodes[i].hostname[0]; i++) {
+ const struct isis_test_node *tnode = &topology->nodes[i];
+ int ret;
+
+ if (i > MAX_NODES) {
+ zlog_debug(
+ "%s: topology has too many nodes (maximum is %u)",
+ __func__, MAX_NODES);
+ return -1;
+ }
+
+ ret = topology_load_node(topology, tnode, i, area, lspdb);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/tests/isisd/test_common.h b/tests/isisd/test_common.h
new file mode 100644
index 0000000..f0c5493
--- /dev/null
+++ b/tests/isisd/test_common.h
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#ifndef _COMMON_ISIS_H
+#define _COMMON_ISIS_H
+
+#include "isisd/isisd.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_spf_private.h"
+
+#define MAX_HOSTNAME 16
+#define MAX_NETWORKS 8
+#define MAX_ADJACENCIES 8
+#define MAX_NODES 12
+
+#define SRGB_DFTL_LOWER_BOUND 16000
+#define SRGB_DFTL_RANGE_SIZE 8000
+
+struct isis_test_adj {
+ char hostname[MAX_HOSTNAME];
+ uint8_t pseudonode_id;
+ uint32_t metric;
+};
+
+struct isis_test_node {
+ char hostname[MAX_HOSTNAME];
+ uint8_t sysid[ISIS_SYS_ID_LEN];
+ uint8_t pseudonode_id;
+ int level;
+ struct {
+ bool ipv4;
+ bool ipv6;
+ } protocols;
+ const char *router_id;
+ struct {
+ uint32_t lower_bound;
+ uint32_t range_size;
+ } srgb;
+ const char *networks[MAX_NETWORKS + 1];
+ struct isis_test_adj adjacencies[MAX_ADJACENCIES + 1];
+ uint8_t flags;
+};
+#define F_ISIS_TEST_NODE_SR 0x01
+
+struct isis_topology {
+ uint16_t number;
+ struct isis_test_node nodes[MAX_NODES + 1];
+};
+
+/* Prototypes. */
+extern int isis_sock_init(struct isis_circuit *circuit);
+extern const struct isis_test_node *
+test_topology_find_node(const struct isis_topology *topology,
+ const char *hostname, uint8_t pseudonode_id);
+extern const struct isis_topology *
+test_topology_find(struct isis_topology *test_topologies, uint16_t number);
+extern mpls_label_t
+test_topology_node_ldp_label(const struct isis_topology *topology,
+ struct in_addr router_id);
+extern int test_topology_load(const struct isis_topology *topology,
+ struct isis_area *area,
+ struct lspdb_head lspdb[]);
+
+/* Global variables. */
+extern struct event_loop *master;
+extern struct zebra_privs_t isisd_privs;
+extern struct isis_topology test_topologies[];
+
+#endif /* _COMMON_ISIS_H */
diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c
new file mode 100644
index 0000000..627ccfe
--- /dev/null
+++ b/tests/isisd/test_fuzz_isis_tlv.c
@@ -0,0 +1,191 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "test_fuzz_isis_tlv_tests.h"
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "sbuf.h"
+#include "stream.h"
+#include "frrevent.h"
+
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_tlvs.h"
+
+#include "test_common.h"
+
+#define TEST_STREAM_SIZE 1500
+
+static bool atexit_registered;
+
+static void show_meminfo_at_exit(void)
+{
+ log_memstats(stderr, "isis fuzztest");
+}
+
+static int comp_line(const void *p1, const void *p2)
+{
+ return strcmp(*(char * const *)p1, *(char * const *)p2);
+}
+
+static char *sortlines(char *in)
+{
+ size_t line_count = 1;
+ size_t rv_len = strlen(in) + 1;
+ size_t rv_pos = 0;
+ char *rv = XMALLOC(MTYPE_TMP, rv_len);
+
+ for (char *c = in; *c; c++) {
+ if (*c == '\n')
+ line_count++;
+ }
+
+ if (line_count == 1) {
+ strncpy(rv, in, rv_len);
+ return rv;
+ }
+
+ char **lines = XCALLOC(MTYPE_TMP, sizeof(char *)*line_count);
+ char *saveptr = NULL;
+ size_t i = 0;
+
+ for (char *line = strtok_r(in, "\n", &saveptr); line;
+ line = strtok_r(NULL, "\n", &saveptr)) {
+ lines[i++] = line;
+ assert(i <= line_count);
+ }
+
+ line_count = i;
+
+ qsort(lines, line_count, sizeof(char *), comp_line);
+
+ for (i = 0; i < line_count; i++) {
+ int printf_rv = snprintf(rv + rv_pos, rv_len - rv_pos, "%s\n", lines[i]);
+ assert(printf_rv >= 0);
+ rv_pos += printf_rv;
+ }
+
+ XFREE(MTYPE_TMP, lines);
+ return rv;
+}
+
+static int test(FILE *input, FILE *output)
+{
+ struct stream *s = stream_new(TEST_STREAM_SIZE);
+ char buf[TEST_STREAM_SIZE];
+ size_t bytes_read = 0;
+
+ if (!atexit_registered) {
+ atexit(show_meminfo_at_exit);
+ atexit_registered = true;
+ }
+
+ while (STREAM_WRITEABLE(s) && !feof(input)) {
+ bytes_read = fread(buf, 1, STREAM_WRITEABLE(s), input);
+ if (bytes_read == 0)
+ break;
+ stream_put(s, buf, bytes_read);
+ }
+
+ if (bytes_read && !feof(input)) {
+ fprintf(output, "Too much input data.\n");
+ stream_free(s);
+ return 1;
+ }
+
+ stream_set_getp(s, 0);
+ struct isis_tlvs *tlvs;
+ const char *log;
+ int rv = isis_unpack_tlvs(STREAM_READABLE(s), s, &tlvs, &log);
+
+ if (rv) {
+ fprintf(output, "Could not unpack TLVs:\n%s\n", log);
+ isis_free_tlvs(tlvs);
+ stream_free(s);
+ return 2;
+ }
+
+ fprintf(output, "Unpack log:\n%s", log);
+ const char *s_tlvs = isis_format_tlvs(tlvs, NULL);
+ fprintf(output, "Unpacked TLVs:\n%s", s_tlvs);
+
+ struct isis_item *orig_auth = tlvs->isis_auth.head;
+ tlvs->isis_auth.head = NULL;
+ s_tlvs = isis_format_tlvs(tlvs, NULL);
+ struct isis_tlvs *tlv_copy = isis_copy_tlvs(tlvs);
+ tlvs->isis_auth.head = orig_auth;
+ isis_free_tlvs(tlvs);
+
+ struct stream *s2 = stream_new(TEST_STREAM_SIZE);
+
+ if (isis_pack_tlvs(tlv_copy, s2, (size_t)-1, false, false)) {
+ fprintf(output, "Could not pack TLVs.\n");
+ assert(0);
+ }
+
+ stream_set_getp(s2, 0);
+ rv = isis_unpack_tlvs(STREAM_READABLE(s2), s2, &tlvs, &log);
+ if (rv) {
+ fprintf(output, "Could not unpack own TLVs:\n%s\n", log);
+ assert(0);
+ }
+
+ char *orig_tlvs = XSTRDUP(MTYPE_TMP, s_tlvs);
+ s_tlvs = isis_format_tlvs(tlvs, NULL);
+
+ if (strcmp(orig_tlvs, s_tlvs)) {
+ fprintf(output,
+ "Deserialized and Serialized LSP seem to differ.\n");
+ fprintf(output, "Re-Unpacked TLVs:\n%s", s_tlvs);
+ assert(0);
+ }
+
+ isis_free_tlvs(tlv_copy);
+ stream_free(s);
+ stream_free(s2);
+
+ struct list *fragments = isis_fragment_tlvs(tlvs, 550);
+ isis_free_tlvs(tlvs);
+ if (!fragments) {
+ XFREE(MTYPE_TMP, orig_tlvs);
+ return 0;
+ }
+
+ s = stream_new(550);
+
+ struct sbuf fragment_format;
+ sbuf_init(&fragment_format, NULL, 0);
+
+ struct listnode *node;
+ for (ALL_LIST_ELEMENTS_RO(fragments, node, tlvs)) {
+ stream_reset(s);
+ int rv = isis_pack_tlvs(tlvs, s, (size_t)-1, false, false);
+ if (rv) {
+ fprintf(output, "Could not pack fragment, too large.\n");
+ assert(0);
+ }
+ sbuf_push(&fragment_format, 0, "%s", isis_format_tlvs(tlvs, NULL));
+ isis_free_tlvs(tlvs);
+ }
+ list_delete(&fragments);
+ stream_free(s);
+
+ char *fragment_content = sortlines((char *)sbuf_buf(&fragment_format));
+ sbuf_free(&fragment_format);
+ char *orig_tlv_content = sortlines(orig_tlvs);
+ XFREE(MTYPE_TMP, orig_tlvs);
+
+ if (strcmp(fragment_content, orig_tlv_content)) {
+ fprintf(output, "Fragmented and unfragmented LSP seem to differ.\n");
+ fprintf(output, "Original:\n%s\nFragmented:\n%s\n",
+ orig_tlv_content, fragment_content);
+ assert(0);
+ }
+
+ XFREE(MTYPE_TMP, fragment_content);
+ XFREE(MTYPE_TMP, orig_tlv_content);
+
+ return 0;
+}
diff --git a/tests/isisd/test_fuzz_isis_tlv.py b/tests/isisd/test_fuzz_isis_tlv.py
new file mode 100644
index 0000000..8fd20aa
--- /dev/null
+++ b/tests/isisd/test_fuzz_isis_tlv.py
@@ -0,0 +1,32 @@
+import frrtest
+
+import pytest
+import platform
+import socket
+
+
+##
+# on musl, ntop compresses a single :0: -> :: which is against RFC
+##
+def inet_ntop_broken():
+ addr = "1:2:3:4:0:6:7:8"
+ return (
+ socket.inet_ntop(socket.AF_INET6, socket.inet_pton(socket.AF_INET6, addr))
+ != addr
+ )
+
+
+if platform.uname()[0] == "SunOS" or inet_ntop_broken():
+
+ class TestFuzzIsisTLV:
+ @pytest.mark.skipif(True, reason="Test unsupported")
+ def test_exit_cleanly(self):
+ pass
+
+
+else:
+
+ class TestFuzzIsisTLV(frrtest.TestMultiOut):
+ program = "./test_fuzz_isis_tlv"
+
+ TestFuzzIsisTLV.exit_cleanly()
diff --git a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz
new file mode 100644
index 0000000..195a7dd
--- /dev/null
+++ b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz
Binary files differ
diff --git a/tests/isisd/test_isis_lspdb.c b/tests/isisd/test_isis_lspdb.c
new file mode 100644
index 0000000..cc95c4a
--- /dev/null
+++ b/tests/isisd/test_isis_lspdb.c
@@ -0,0 +1,81 @@
+#include <zebra.h>
+
+#include "isisd/isis_lsp.c"
+
+#include "test_common.h"
+
+static void test_lsp_build_list_nonzero_ht(void)
+{
+ uint8_t lsp_id1[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00
+ };
+ uint8_t lsp_id_end[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x5f, 0x00
+ };
+ uint8_t lsp_id2[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00
+ };
+
+ struct isis_area *area = calloc(sizeof(*area), 1);
+
+ area->lsp_mtu = 1500;
+
+ struct lspdb_head _lspdb, *lspdb = &_lspdb;
+ lsp_db_init(&_lspdb);
+
+ struct isis_lsp *lsp1 =
+ lsp_new(area, lsp_id1, 6000, 1, 0, 0, NULL, ISIS_LEVEL2);
+
+ lspdb_add(lspdb, lsp1);
+
+ struct isis_lsp *lsp2 =
+ lsp_new(area, lsp_id2, 6000, 1, 0, 0, NULL, ISIS_LEVEL2);
+
+ lspdb_add(lspdb, lsp2);
+
+ struct list *list = list_new();
+
+ lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list);
+ assert(list->count == 1);
+ assert(listgetdata(listhead(list)) == lsp1);
+ list_delete_all_node(list);
+
+ lsp_id_end[5] = 0x03;
+ lsp_id_end[6] = 0x00;
+
+ lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list);
+ assert(list->count == 2);
+ assert(listgetdata(listhead(list)) == lsp1);
+ assert(listgetdata(listtail(list)) == lsp2);
+ list_delete_all_node(list);
+
+ memcpy(lsp_id1, lsp_id2, sizeof(lsp_id1));
+
+ lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list);
+ assert(list->count == 1);
+ assert(listgetdata(listhead(list)) == lsp2);
+ list_delete_all_node(list);
+
+ lsp_id1[5] = 0x03;
+ lsp_id_end[5] = 0x04;
+
+ lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list);
+ assert(list->count == 0);
+ list_delete_all_node(list);
+
+ lsp_id1[5] = 0x00;
+
+ lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list);
+ assert(list->count == 2);
+ assert(listgetdata(listhead(list)) == lsp1);
+ assert(listgetdata(listtail(list)) == lsp2);
+ list_delete_all_node(list);
+}
+
+int main(int argc, char **argv)
+{
+ struct isis *isis = NULL;
+ isis = calloc(sizeof(*isis), 1);
+ test_lsp_build_list_nonzero_ht();
+ return 0;
+}
diff --git a/tests/isisd/test_isis_lspdb.py b/tests/isisd/test_isis_lspdb.py
new file mode 100644
index 0000000..280ed1c
--- /dev/null
+++ b/tests/isisd/test_isis_lspdb.py
@@ -0,0 +1,8 @@
+import frrtest
+
+
+class TestIsisLSPDB(frrtest.TestMultiOut):
+ program = "./test_isis_lspdb"
+
+
+TestIsisLSPDB.exit_cleanly()
diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c
new file mode 100644
index 0000000..6eb180b
--- /dev/null
+++ b/tests/isisd/test_isis_spf.c
@@ -0,0 +1,561 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "frrevent.h"
+#include "vty.h"
+#include "command.h"
+#include "log.h"
+#include "vrf.h"
+#include "yang.h"
+
+#include "isisd/isisd.h"
+#include "isisd/isis_dynhn.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_route.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_spf_private.h"
+
+#include "test_common.h"
+
+enum test_type {
+ TEST_SPF = 1,
+ TEST_REVERSE_SPF,
+ TEST_LFA,
+ TEST_RLFA,
+ TEST_TI_LFA,
+};
+
+#define F_DISPLAY_LSPDB 0x01
+#define F_IPV4_ONLY 0x02
+#define F_IPV6_ONLY 0x04
+#define F_LEVEL1_ONLY 0x08
+#define F_LEVEL2_ONLY 0x10
+
+static void test_run_spf(struct vty *vty, const struct isis_topology *topology,
+ const struct isis_test_node *root,
+ struct isis_area *area, struct lspdb_head *lspdb,
+ int level, int tree, bool reverse)
+{
+ struct isis_spftree *spftree;
+ enum spf_type spf_type;
+
+ /* Run SPF. */
+ spf_type = reverse ? SPF_TYPE_REVERSE : SPF_TYPE_FORWARD;
+ spftree = isis_spftree_new(area, lspdb, root->sysid, level, tree,
+ spf_type, F_SPFTREE_NO_ADJACENCIES,
+ SR_ALGORITHM_SPF);
+ isis_run_spf(spftree);
+
+ /* Print the SPT and the corresponding routing table. */
+ isis_print_spftree(vty, spftree);
+ isis_print_routes(vty, spftree, NULL, false, false);
+
+ /* Cleanup SPF tree. */
+ isis_spftree_del(spftree);
+}
+
+static void test_run_lfa(struct vty *vty, const struct isis_topology *topology,
+ const struct isis_test_node *root,
+ struct isis_area *area, struct lspdb_head *lspdb,
+ int level, int tree,
+ struct lfa_protected_resource *protected_resource)
+{
+ struct isis_spftree *spftree_self;
+ uint8_t flags;
+
+ /* Run forward SPF in the root node. */
+ flags = F_SPFTREE_NO_ADJACENCIES;
+ spftree_self =
+ isis_spftree_new(area, lspdb, root->sysid, level, tree,
+ SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF);
+ isis_run_spf(spftree_self);
+
+ /* Run forward SPF on all adjacent routers. */
+ isis_spf_run_neighbors(spftree_self);
+
+ /* Compute the LFA repair paths. */
+ isis_lfa_compute(area, NULL, spftree_self, protected_resource);
+
+ /* Print the SPT and the corresponding main/backup routing tables. */
+ isis_print_spftree(vty, spftree_self);
+ vty_out(vty, "Main:\n");
+ isis_print_routes(vty, spftree_self, NULL, false, false);
+ vty_out(vty, "Backup:\n");
+ isis_print_routes(vty, spftree_self, NULL, false, true);
+
+ /* Cleanup everything. */
+ isis_spftree_del(spftree_self);
+}
+
+static void test_run_rlfa(struct vty *vty, const struct isis_topology *topology,
+ const struct isis_test_node *root,
+ struct isis_area *area, struct lspdb_head *lspdb,
+ int level, int tree,
+ struct lfa_protected_resource *protected_resource)
+{
+ struct isis_spftree *spftree_self;
+ struct isis_spftree *spftree_reverse;
+ struct isis_spftree *spftree_pc;
+ struct isis_spf_node *spf_node, *node;
+ struct rlfa *rlfa;
+ uint8_t flags;
+
+ /* Run forward SPF in the root node. */
+ flags = F_SPFTREE_NO_ADJACENCIES;
+ spftree_self =
+ isis_spftree_new(area, lspdb, root->sysid, level, tree,
+ SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF);
+ isis_run_spf(spftree_self);
+
+ /* Run reverse SPF in the root node. */
+ spftree_reverse = isis_spf_reverse_run(spftree_self);
+
+ /* Run forward SPF on all adjacent routers. */
+ isis_spf_run_neighbors(spftree_self);
+
+ /* Compute the local LFA repair paths. */
+ isis_lfa_compute(area, NULL, spftree_self, protected_resource);
+
+ /* Compute the remote LFA repair paths. */
+ spftree_pc = isis_rlfa_compute(area, spftree_self, spftree_reverse, 0,
+ protected_resource);
+
+ /* Print the extended P-space and Q-space. */
+ vty_out(vty, "P-space (self):\n");
+ RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.p_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+ RB_FOREACH (spf_node, isis_spf_nodes, &spftree_self->adj_nodes) {
+ if (RB_EMPTY(isis_spf_nodes, &spf_node->lfa.p_space))
+ continue;
+ vty_out(vty, "P-space (%s):\n",
+ print_sys_hostname(spf_node->sysid));
+ RB_FOREACH (node, isis_spf_nodes, &spf_node->lfa.p_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+ }
+ vty_out(vty, "Q-space:\n");
+ RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.q_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+
+ /* Print the post-convergence SPT. */
+ isis_print_spftree(vty, spftree_pc);
+
+ /*
+ * Activate the computed RLFAs (if any) using artificial LDP labels for
+ * the PQ nodes.
+ */
+ frr_each_safe (rlfa_tree, &spftree_self->lfa.remote.rlfas, rlfa) {
+ struct zapi_rlfa_response response = {};
+
+ response.pq_label = test_topology_node_ldp_label(
+ topology, rlfa->pq_address);
+ assert(response.pq_label != MPLS_INVALID_LABEL);
+ isis_rlfa_activate(spftree_self, rlfa, &response);
+ }
+
+ /* Print the SPT and the corresponding main/backup routing tables. */
+ isis_print_spftree(vty, spftree_self);
+ vty_out(vty, "Main:\n");
+ isis_print_routes(vty, spftree_self, NULL, false, false);
+ vty_out(vty, "Backup:\n");
+ isis_print_routes(vty, spftree_self, NULL, false, true);
+
+ /* Cleanup everything. */
+ isis_spftree_del(spftree_self);
+ isis_spftree_del(spftree_reverse);
+ isis_spftree_del(spftree_pc);
+}
+
+static void test_run_ti_lfa(struct vty *vty,
+ const struct isis_topology *topology,
+ const struct isis_test_node *root,
+ struct isis_area *area, struct lspdb_head *lspdb,
+ int level, int tree,
+ struct lfa_protected_resource *protected_resource)
+{
+ struct isis_spftree *spftree_self;
+ struct isis_spftree *spftree_reverse;
+ struct isis_spftree *spftree_pc;
+ struct isis_spf_node *spf_node, *node;
+ uint8_t flags;
+
+ /* Run forward SPF in the root node. */
+ flags = F_SPFTREE_NO_ADJACENCIES;
+ spftree_self =
+ isis_spftree_new(area, lspdb, root->sysid, level, tree,
+ SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF);
+ isis_run_spf(spftree_self);
+
+ /* Run reverse SPF in the root node. */
+ spftree_reverse = isis_spf_reverse_run(spftree_self);
+
+ /* Run forward SPF on all adjacent routers. */
+ isis_spf_run_neighbors(spftree_self);
+
+ /* Compute the TI-LFA repair paths. */
+ spftree_pc = isis_tilfa_compute(area, spftree_self, spftree_reverse,
+ protected_resource);
+
+ /* Print the extended P-space and Q-space. */
+ vty_out(vty, "P-space (self):\n");
+ RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.p_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+ RB_FOREACH (spf_node, isis_spf_nodes, &spftree_self->adj_nodes) {
+ if (RB_EMPTY(isis_spf_nodes, &spf_node->lfa.p_space))
+ continue;
+ vty_out(vty, "P-space (%s):\n",
+ print_sys_hostname(spf_node->sysid));
+ RB_FOREACH (node, isis_spf_nodes, &spf_node->lfa.p_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+ }
+ vty_out(vty, "Q-space:\n");
+ RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.q_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+
+ /*
+ * Print the post-convergence SPT and the corresponding routing table.
+ */
+ isis_print_spftree(vty, spftree_pc);
+ isis_print_routes(vty, spftree_self, NULL, false, true);
+
+ /* Cleanup everything. */
+ isis_spftree_del(spftree_self);
+ isis_spftree_del(spftree_reverse);
+ isis_spftree_del(spftree_pc);
+}
+
+static int test_run(struct vty *vty, const struct isis_topology *topology,
+ const struct isis_test_node *root, enum test_type test_type,
+ uint8_t flags, enum lfa_protection_type protection_type,
+ const char *fail_sysid_str, uint8_t fail_pseudonode_id)
+{
+ struct isis_area *area;
+ struct lfa_protected_resource protected_resource = {};
+ uint8_t fail_id[ISIS_SYS_ID_LEN] = {};
+
+ /* Init topology. */
+ area = isis_area_create("1", NULL);
+ memcpy(area->isis->sysid, root->sysid, sizeof(area->isis->sysid));
+ area->is_type = IS_LEVEL_1_AND_2;
+ area->srdb.enabled = true;
+ if (test_topology_load(topology, area, area->lspdb) != 0) {
+ vty_out(vty, "%% Failed to load topology\n");
+ return CMD_WARNING;
+ }
+
+ /* Parse failed link/node. */
+ if (fail_sysid_str) {
+ if (sysid2buff(fail_id, fail_sysid_str) == 0) {
+ struct isis_dynhn *dynhn;
+
+ dynhn = dynhn_find_by_name(area->isis, fail_sysid_str);
+ if (dynhn == NULL) {
+ vty_out(vty, "Invalid system id %s\n",
+ fail_sysid_str);
+ return CMD_WARNING;
+ }
+ memcpy(fail_id, dynhn->id, ISIS_SYS_ID_LEN);
+ }
+
+ protected_resource.type = protection_type;
+ memcpy(protected_resource.adjacency, fail_id, ISIS_SYS_ID_LEN);
+ LSP_PSEUDO_ID(protected_resource.adjacency) =
+ fail_pseudonode_id;
+ }
+
+ for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) {
+ if (level == IS_LEVEL_1 && CHECK_FLAG(flags, F_LEVEL2_ONLY))
+ continue;
+ if (level == IS_LEVEL_2 && CHECK_FLAG(flags, F_LEVEL1_ONLY))
+ continue;
+ if ((root->level & level) == 0)
+ continue;
+
+ /* Print the LDPDB. */
+ if (CHECK_FLAG(flags, F_DISPLAY_LSPDB))
+ show_isis_database_lspdb_vty(vty, area, level - 1,
+ &area->lspdb[level - 1], NULL,
+ ISIS_UI_LEVEL_DETAIL);
+
+ for (int tree = SPFTREE_IPV4; tree <= SPFTREE_IPV6; tree++) {
+ if (tree == SPFTREE_IPV4
+ && CHECK_FLAG(flags, F_IPV6_ONLY))
+ continue;
+ if (tree == SPFTREE_IPV6
+ && CHECK_FLAG(flags, F_IPV4_ONLY))
+ continue;
+
+ switch (test_type) {
+ case TEST_SPF:
+ test_run_spf(vty, topology, root, area,
+ &area->lspdb[level - 1], level,
+ tree, false);
+ break;
+ case TEST_REVERSE_SPF:
+ test_run_spf(vty, topology, root, area,
+ &area->lspdb[level - 1], level,
+ tree, true);
+ break;
+ case TEST_LFA:
+ test_run_lfa(vty, topology, root, area,
+ &area->lspdb[level - 1], level,
+ tree, &protected_resource);
+ break;
+ case TEST_RLFA:
+ test_run_rlfa(vty, topology, root, area,
+ &area->lspdb[level - 1], level,
+ tree, &protected_resource);
+ break;
+ case TEST_TI_LFA:
+ test_run_ti_lfa(vty, topology, root, area,
+ &area->lspdb[level - 1], level,
+ tree, &protected_resource);
+ break;
+ }
+ }
+ }
+
+ /* Cleanup IS-IS area. */
+ isis_area_destroy(area);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(test_isis, test_isis_cmd,
+ "test isis topology (1-14) root HOSTNAME\
+ <\
+ spf\
+ |reverse-spf\
+ |lfa system-id WORD [pseudonode-id <1-255>]\
+ |remote-lfa system-id WORD [pseudonode-id <1-255>]\
+ |ti-lfa system-id WORD [pseudonode-id <1-255>] [node-protection]\
+ >\
+ [display-lspdb] [<ipv4-only|ipv6-only>] [<level-1-only|level-2-only>]",
+ "Test command\n"
+ "IS-IS routing protocol\n"
+ "Test topology\n"
+ "Test topology number\n"
+ "SPF root\n"
+ "SPF root hostname\n"
+ "Normal Shortest Path First\n"
+ "Reverse Shortest Path First\n"
+ "Classic LFA\n"
+ "System ID\n"
+ "System ID\n"
+ "Pseudonode-ID\n"
+ "Pseudonode-ID\n"
+ "Remote LFA\n"
+ "System ID\n"
+ "System ID\n"
+ "Pseudonode-ID\n"
+ "Pseudonode-ID\n"
+ "Topology Independent LFA\n"
+ "System ID\n"
+ "System ID\n"
+ "Pseudonode-ID\n"
+ "Pseudonode-ID\n"
+ "Node protection\n"
+ "Display the LSPDB\n"
+ "Do IPv4 processing only\n"
+ "Do IPv6 processing only\n"
+ "Skip L2 LSPs\n"
+ "Skip L1 LSPs\n")
+{
+ uint16_t topology_number;
+ const struct isis_topology *topology;
+ const struct isis_test_node *root;
+ enum test_type test_type;
+ enum lfa_protection_type protection_type = 0;
+ const char *fail_sysid_str = NULL;
+ uint8_t fail_pseudonode_id = 0;
+ uint8_t flags = 0;
+ int idx = 0;
+
+ /* Load topology. */
+ argv_find(argv, argc, "topology", &idx);
+ topology_number = atoi(argv[idx + 1]->arg);
+ topology = test_topology_find(test_topologies, topology_number);
+ if (!topology) {
+ vty_out(vty, "%% Topology \"%s\" not found\n",
+ argv[idx + 1]->arg);
+ return CMD_WARNING;
+ }
+
+ /* Find root node. */
+ argv_find(argv, argc, "root", &idx);
+ root = test_topology_find_node(topology, argv[idx + 1]->arg, 0);
+ if (!root) {
+ vty_out(vty, "%% Node \"%s\" not found\n", argv[idx + 1]->arg);
+ return CMD_WARNING;
+ }
+
+ /* Parse test information. */
+ if (argv_find(argv, argc, "spf", &idx))
+ test_type = TEST_SPF;
+ else if (argv_find(argv, argc, "reverse-spf", &idx))
+ test_type = TEST_REVERSE_SPF;
+ else if (argv_find(argv, argc, "lfa", &idx)) {
+ test_type = TEST_LFA;
+
+ fail_sysid_str = argv[idx + 2]->arg;
+ if (argv_find(argv, argc, "pseudonode-id", &idx))
+ fail_pseudonode_id =
+ strtoul(argv[idx + 1]->arg, NULL, 10);
+ protection_type = LFA_LINK_PROTECTION;
+ } else if (argv_find(argv, argc, "remote-lfa", &idx)) {
+ test_type = TEST_RLFA;
+
+ fail_sysid_str = argv[idx + 2]->arg;
+ if (argv_find(argv, argc, "pseudonode-id", &idx))
+ fail_pseudonode_id =
+ strtoul(argv[idx + 1]->arg, NULL, 10);
+ protection_type = LFA_LINK_PROTECTION;
+ } else if (argv_find(argv, argc, "ti-lfa", &idx)) {
+ test_type = TEST_TI_LFA;
+
+ fail_sysid_str = argv[idx + 2]->arg;
+ if (argv_find(argv, argc, "pseudonode-id", &idx))
+ fail_pseudonode_id =
+ strtoul(argv[idx + 1]->arg, NULL, 10);
+ if (argv_find(argv, argc, "node-protection", &idx))
+ protection_type = LFA_NODE_PROTECTION;
+ else
+ protection_type = LFA_LINK_PROTECTION;
+ } else
+ return CMD_WARNING;
+
+ /* Parse control flags. */
+ if (argv_find(argv, argc, "display-lspdb", &idx))
+ SET_FLAG(flags, F_DISPLAY_LSPDB);
+ if (argv_find(argv, argc, "ipv4-only", &idx))
+ SET_FLAG(flags, F_IPV4_ONLY);
+ else if (argv_find(argv, argc, "ipv6-only", &idx))
+ SET_FLAG(flags, F_IPV6_ONLY);
+ if (argv_find(argv, argc, "level-1-only", &idx))
+ SET_FLAG(flags, F_LEVEL1_ONLY);
+ else if (argv_find(argv, argc, "level-2-only", &idx))
+ SET_FLAG(flags, F_LEVEL2_ONLY);
+
+ return test_run(vty, topology, root, test_type, flags, protection_type,
+ fail_sysid_str, fail_pseudonode_id);
+}
+
+static void vty_do_exit(int isexit)
+{
+ printf("\nend.\n");
+
+ cmd_terminate();
+ vty_terminate();
+ yang_terminate();
+ event_master_free(master);
+
+ log_memstats(stderr, "test-isis-spf");
+ if (!isexit)
+ exit(0);
+}
+
+struct option longopts[] = {{"help", no_argument, NULL, 'h'},
+ {"debug", no_argument, NULL, 'd'},
+ {0}};
+
+/* Help information display. */
+static void usage(char *progname, int status)
+{
+ if (status != 0)
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ progname);
+ else {
+ printf("Usage : %s [OPTION...]\n\
+isisd SPF test program.\n\n\
+-u, --debug Enable debugging\n\
+-h, --help Display this help and exit\n\
+\n\
+Report bugs to %s\n",
+ progname, FRR_BUG_ADDRESS);
+ }
+ exit(status);
+}
+
+int main(int argc, char **argv)
+{
+ char *p;
+ char *progname;
+ struct event thread;
+ bool debug = false;
+
+ /* Set umask before anything for security */
+ umask(0027);
+
+ /* get program name */
+ progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
+
+ while (1) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "hd", longopts, 0);
+
+ if (opt == EOF)
+ break;
+
+ switch (opt) {
+ case 0:
+ break;
+ case 'd':
+ debug = true;
+ break;
+ case 'h':
+ usage(progname, 0);
+ break;
+ default:
+ usage(progname, 1);
+ break;
+ }
+ }
+
+ /* master init. */
+ master = event_master_create(NULL);
+ isis_master_init(master);
+
+ /* Library inits. */
+ cmd_init(1);
+ cmd_hostname_set("test");
+ vty_init(master, false);
+ yang_init(true, false);
+ if (debug)
+ zlog_aux_init("NONE: ", LOG_DEBUG);
+ else
+ zlog_aux_init("NONE: ", ZLOG_DISABLED);
+
+ /* IS-IS inits. */
+ yang_module_load("frr-isisd");
+ SET_FLAG(im->options, F_ISIS_UNIT_TEST);
+ debug_spf_events |= DEBUG_SPF_EVENTS;
+ debug_lfa |= DEBUG_LFA;
+ debug_events |= DEBUG_EVENTS;
+ debug_rte_events |= DEBUG_RTE_EVENTS;
+
+ /* Install test command. */
+ install_element(VIEW_NODE, &test_isis_cmd);
+
+ /* Read input from .in file. */
+ vty_stdio(vty_do_exit);
+
+ /* Fetch next active thread. */
+ while (event_fetch(master, &thread))
+ event_call(&thread);
+
+ /* Not reached. */
+ exit(0);
+}
diff --git a/tests/isisd/test_isis_spf.in b/tests/isisd/test_isis_spf.in
new file mode 100644
index 0000000..f8f65ff
--- /dev/null
+++ b/tests/isisd/test_isis_spf.in
@@ -0,0 +1,68 @@
+test isis topology 1 root rt1 spf
+test isis topology 2 root rt1 spf
+test isis topology 3 root rt1 spf ipv4-only
+test isis topology 4 root rt1 spf ipv4-only
+test isis topology 5 root rt1 spf ipv4-only
+test isis topology 6 root rt1 spf ipv4-only
+test isis topology 7 root rt1 spf ipv4-only
+test isis topology 8 root rt1 spf ipv4-only
+test isis topology 9 root rt1 spf
+test isis topology 10 root rt1 spf
+test isis topology 11 root rt1 spf
+test isis topology 12 root rt1 spf ipv4-only
+test isis topology 13 root rt1 spf ipv4-only
+
+test isis topology 4 root rt1 reverse-spf ipv4-only
+test isis topology 11 root rt1 reverse-spf
+
+test isis topology 1 root rt1 lfa system-id rt2
+test isis topology 2 root rt4 lfa system-id rt1 pseudonode-id 1
+test isis topology 2 root rt4 lfa system-id rt6
+test isis topology 3 root rt1 lfa system-id rt2
+test isis topology 3 root rt1 lfa system-id rt3
+test isis topology 7 root rt1 lfa system-id rt4
+test isis topology 7 root rt7 lfa system-id rt8
+test isis topology 7 root rt8 lfa system-id rt11
+test isis topology 9 root rt3 lfa system-id rt1
+test isis topology 10 root rt8 lfa system-id rt5
+test isis topology 11 root rt3 lfa system-id rt5
+test isis topology 13 root rt4 lfa system-id rt3
+test isis topology 14 root rt1 lfa system-id rt1 pseudonode-id 1
+test isis topology 14 root rt1 lfa system-id rt2
+test isis topology 14 root rt5 lfa system-id rt4
+
+test isis topology 1 root rt1 remote-lfa system-id rt2
+test isis topology 2 root rt5 remote-lfa system-id rt1 pseudonode-id 1
+test isis topology 3 root rt5 remote-lfa system-id rt4 ipv4-only
+test isis topology 3 root rt5 remote-lfa system-id rt3 ipv4-only
+test isis topology 5 root rt1 remote-lfa system-id rt2 ipv4-only
+test isis topology 6 root rt4 remote-lfa system-id rt3 ipv4-only
+test isis topology 7 root rt11 remote-lfa system-id rt8 ipv4-only
+test isis topology 7 root rt6 remote-lfa system-id rt5 ipv4-only
+test isis topology 8 root rt2 remote-lfa system-id rt5 ipv4-only
+test isis topology 11 root rt2 remote-lfa system-id rt4
+test isis topology 13 root rt1 remote-lfa system-id rt3 ipv4-only
+
+test isis topology 1 root rt1 ti-lfa system-id rt2
+test isis topology 2 root rt1 ti-lfa system-id rt3
+test isis topology 2 root rt1 ti-lfa system-id rt1 pseudonode-id 1
+test isis topology 2 root rt5 ti-lfa system-id rt1 pseudonode-id 1
+test isis topology 3 root rt5 ti-lfa system-id rt4 ipv4-only
+test isis topology 3 root rt5 ti-lfa system-id rt3 ipv4-only
+test isis topology 4 root rt1 ti-lfa system-id rt2 ipv4-only
+test isis topology 4 root rt4 ti-lfa system-id rt6 ipv4-only
+test isis topology 5 root rt1 ti-lfa system-id rt2 ipv4-only
+test isis topology 6 root rt4 ti-lfa system-id rt3 ipv4-only
+test isis topology 7 root rt11 ti-lfa system-id rt8 ipv4-only
+test isis topology 7 root rt6 ti-lfa system-id rt5 ipv4-only
+test isis topology 8 root rt2 ti-lfa system-id rt1 ipv4-only
+test isis topology 8 root rt2 ti-lfa system-id rt5 ipv4-only
+test isis topology 9 root rt1 ti-lfa system-id rt3
+test isis topology 9 root rt1 ti-lfa system-id rt2
+test isis topology 9 root rt9 ti-lfa system-id rt5
+test isis topology 9 root rt9 ti-lfa system-id rt8
+test isis topology 10 root rt1 ti-lfa system-id rt2
+test isis topology 10 root rt1 ti-lfa system-id rt4
+test isis topology 11 root rt2 ti-lfa system-id rt4
+test isis topology 12 root rt1 ti-lfa system-id rt3 ipv4-only
+test isis topology 13 root rt1 ti-lfa system-id rt3 ipv4-only
diff --git a/tests/isisd/test_isis_spf.py b/tests/isisd/test_isis_spf.py
new file mode 100644
index 0000000..f44fa70
--- /dev/null
+++ b/tests/isisd/test_isis_spf.py
@@ -0,0 +1,5 @@
+import frrtest
+
+
+class TestIsisSPF(frrtest.TestRefOut):
+ program = "./test_isis_spf"
diff --git a/tests/isisd/test_isis_spf.refout b/tests/isisd/test_isis_spf.refout
new file mode 100644
index 0000000..23d41b9
--- /dev/null
+++ b/tests/isisd/test_isis_spf.refout
@@ -0,0 +1,4800 @@
+test# test isis topology 1 root rt1 spf
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+ rt3 -
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 40 - rt2 16060
+ - rt3 16060
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+2001:db8::6/128 IP6 internal 40 rt2 - rt6(4)
+ rt3 -
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::2/128 20 - rt2 implicit-null
+ 2001:db8::3/128 20 - rt3 implicit-null
+ 2001:db8::4/128 30 - rt2 16041
+ 2001:db8::5/128 30 - rt3 16051
+ 2001:db8::6/128 40 - rt2 16061
+ - rt3 16061
+
+test# test isis topology 2 root rt1 spf
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt5 TE-IS 10 rt5 - rt1(4)
+rt2 TE-IS 15 rt2 - rt1(4)
+rt1
+rt6 TE-IS 20 rt4 - rt4(4)
+ rt5 - rt5(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+10.0.255.2/32 IP TE 25 rt2 - rt2(4)
+rt3 TE-IS 30 rt3 - rt1(4)
+10.0.255.6/32 IP TE 30 rt4 - rt6(4)
+ rt5 -
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 25 - rt2 implicit-null
+ 10.0.255.3/32 40 - rt3 implicit-null
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 30 - rt4 16060
+ - rt5 16060
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt5 TE-IS 10 rt5 - rt1(4)
+rt2 TE-IS 15 rt2 - rt1(4)
+rt1
+rt6 TE-IS 20 rt4 - rt4(4)
+ rt5 - rt5(4)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+2001:db8::5/128 IP6 internal 20 rt5 - rt5(4)
+2001:db8::2/128 IP6 internal 25 rt2 - rt2(4)
+rt3 TE-IS 30 rt3 - rt1(4)
+2001:db8::6/128 IP6 internal 30 rt4 - rt6(4)
+ rt5 -
+2001:db8::3/128 IP6 internal 40 rt3 - rt3(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::2/128 25 - rt2 implicit-null
+ 2001:db8::3/128 40 - rt3 implicit-null
+ 2001:db8::4/128 20 - rt4 implicit-null
+ 2001:db8::5/128 20 - rt5 implicit-null
+ 2001:db8::6/128 30 - rt4 16061
+ - rt5 16061
+
+test# test isis topology 3 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt5 TE-IS 30 rt2 - rt4(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 40 rt2 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 40 - rt2 16050
+ 10.0.255.6/32 40 - rt2 16060
+
+test# test isis topology 4 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt2 - rt6(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 40 - rt2 16060
+ 10.0.255.7/32 40 - rt3 16070
+ 10.0.255.8/32 50 - rt2 16080
+
+test# test isis topology 5 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt2 - rt6(4)
+ rt3 - rt7(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)
+ rt3 -
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 40 - rt2 16060
+ 10.0.255.7/32 40 - rt3 16070
+ 10.0.255.8/32 50 - rt2 16080
+ - rt3 16080
+
+test# test isis topology 6 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+ rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 -
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+ rt3 -
+rt5 TE-IS 40 rt2 - rt6(4)
+ rt3 -
+rt8 TE-IS 40 rt2 - rt6(4)
+ rt3 -
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+ rt3 -
+rt7 TE-IS 50 rt2 - rt5(4)
+ rt3 - rt8(4)
+10.0.255.5/32 IP TE 50 rt2 - rt5(4)
+ rt3 -
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)
+ rt3 -
+10.0.255.7/32 IP TE 60 rt2 - rt7(4)
+ rt3 -
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ - rt3 16040
+ 10.0.255.5/32 50 - rt2 16050
+ - rt3 16050
+ 10.0.255.6/32 40 - rt2 16060
+ - rt3 16060
+ 10.0.255.7/32 60 - rt2 16070
+ - rt3 16070
+ 10.0.255.8/32 50 - rt2 16080
+ - rt3 16080
+
+test# test isis topology 7 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt5 TE-IS 20 rt4 - rt4(4)
+rt7 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+rt2 TE-IS 30 rt4 - rt5(4)
+rt6 TE-IS 30 rt4 - rt5(4)
+rt8 TE-IS 30 rt4 - rt5(4)
+ rt7(4)
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)
+10.0.255.7/32 IP TE 30 rt4 - rt7(4)
+rt10 TE-IS 40 rt4 - rt7(4)
+rt3 TE-IS 40 rt4 - rt2(4)
+ rt6(4)
+rt9 TE-IS 40 rt4 - rt8(4)
+rt11 TE-IS 40 rt4 - rt8(4)
+10.0.255.2/32 IP TE 40 rt4 - rt2(4)
+10.0.255.6/32 IP TE 40 rt4 - rt6(4)
+10.0.255.8/32 IP TE 40 rt4 - rt8(4)
+rt12 TE-IS 50 rt4 - rt9(4)
+ rt11(4)
+10.0.255.10/32 IP TE 50 rt4 - rt10(4)
+10.0.255.3/32 IP TE 50 rt4 - rt3(4)
+10.0.255.9/32 IP TE 50 rt4 - rt9(4)
+10.0.255.11/32 IP TE 50 rt4 - rt11(4)
+10.0.255.12/32 IP TE 60 rt4 - rt12(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 40 - rt4 16020
+ 10.0.255.3/32 50 - rt4 16030
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 30 - rt4 16050
+ 10.0.255.6/32 40 - rt4 16060
+ 10.0.255.7/32 30 - rt4 16070
+ 10.0.255.8/32 40 - rt4 16080
+ 10.0.255.9/32 50 - rt4 16090
+ 10.0.255.10/32 50 - rt4 16100
+ 10.0.255.11/32 50 - rt4 16110
+ 10.0.255.12/32 60 - rt4 16120
+
+test# test isis topology 8 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt3 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt2 - rt2(4)
+rt7 TE-IS 20 rt4 - rt4(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+rt6 TE-IS 30 rt2 - rt3(4)
+ rt5(4)
+rt8 TE-IS 30 rt2 - rt5(4)
+rt10 TE-IS 30 rt4 - rt7(4)
+10.0.255.3/32 IP TE 30 rt2 - rt3(4)
+10.0.255.5/32 IP TE 30 rt2 - rt5(4)
+10.0.255.7/32 IP TE 30 rt4 - rt7(4)
+rt9 TE-IS 40 rt2 - rt8(4)
+rt11 TE-IS 40 rt2 - rt8(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+10.0.255.8/32 IP TE 40 rt2 - rt8(4)
+10.0.255.10/32 IP TE 40 rt4 - rt10(4)
+rt12 TE-IS 50 rt2 - rt9(4)
+ rt11(4)
+10.0.255.9/32 IP TE 50 rt2 - rt9(4)
+10.0.255.11/32 IP TE 50 rt2 - rt11(4)
+10.0.255.12/32 IP TE 60 rt2 - rt12(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 30 - rt2 16030
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 30 - rt2 16050
+ 10.0.255.6/32 40 - rt2 16060
+ 10.0.255.7/32 30 - rt4 16070
+ 10.0.255.8/32 40 - rt2 16080
+ 10.0.255.9/32 50 - rt2 16090
+ 10.0.255.10/32 40 - rt4 16100
+ 10.0.255.11/32 50 - rt2 16110
+ 10.0.255.12/32 60 - rt2 16120
+
+test# test isis topology 9 root rt1 spf
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt5 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+rt9 TE-IS 40 rt2 - rt5(4)
+10.0.255.5/32 IP TE 40 rt2 - rt5(4)
+rt6 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+rt7 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+rt8 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+10.0.255.9/32 IP TE 50 rt2 - rt9(4)
+10.0.255.6/32 IP TE 60 rt2 - rt6(4)
+10.0.255.7/32 IP TE 60 rt2 - rt7(4)
+10.0.255.8/32 IP TE 60 rt2 - rt8(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 40 - rt2 16050
+ 10.0.255.6/32 60 - rt2 16060
+ 10.0.255.7/32 60 - rt2 16070
+ 10.0.255.8/32 60 - rt2 16080
+ 10.0.255.9/32 50 - rt2 16090
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt5 TE-IS 30 rt2 - rt4(4)
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)
+rt9 TE-IS 40 rt2 - rt5(4)
+2001:db8::5/128 IP6 internal 40 rt2 - rt5(4)
+rt6 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+rt7 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+rt8 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+2001:db8::9/128 IP6 internal 50 rt2 - rt9(4)
+2001:db8::6/128 IP6 internal 60 rt2 - rt6(4)
+2001:db8::7/128 IP6 internal 60 rt2 - rt7(4)
+2001:db8::8/128 IP6 internal 60 rt2 - rt8(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::2/128 20 - rt2 implicit-null
+ 2001:db8::3/128 20 - rt3 implicit-null
+ 2001:db8::4/128 30 - rt2 16041
+ 2001:db8::5/128 40 - rt2 16051
+ 2001:db8::6/128 60 - rt2 16061
+ 2001:db8::7/128 60 - rt2 16071
+ 2001:db8::8/128 60 - rt2 16081
+ 2001:db8::9/128 50 - rt2 16091
+
+test# test isis topology 10 root rt1 spf
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 20 rt3 - rt1(4)
+rt4 TE-IS 20 rt4 - rt1(4)
+rt5 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+rt6 TE-IS 30 rt3 - rt3(4)
+rt7 TE-IS 30 rt4 - rt4(4)
+rt8 TE-IS 30 rt2 - rt5(4)
+10.0.255.3/32 IP TE 30 rt3 - rt3(4)
+10.0.255.4/32 IP TE 30 rt4 - rt4(4)
+10.0.255.5/32 IP TE 30 rt2 - rt5(4)
+10.0.255.6/32 IP TE 40 rt3 - rt6(4)
+10.0.255.7/32 IP TE 40 rt4 - rt7(4)
+10.0.255.8/32 IP TE 40 rt2 - rt8(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 30 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt4 implicit-null
+ 10.0.255.5/32 30 - rt2 16050
+ 10.0.255.6/32 40 - rt3 20060
+ 10.0.255.7/32 40 - rt4 16070
+ 10.0.255.8/32 40 - rt2 16080
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 20 rt3 - rt1(4)
+rt4 TE-IS 20 rt4 - rt1(4)
+rt5 TE-IS 20 rt2 - rt2(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+rt6 TE-IS 30 rt3 - rt3(4)
+rt7 TE-IS 30 rt4 - rt4(4)
+rt8 TE-IS 30 rt2 - rt5(4)
+2001:db8::3/128 IP6 internal 30 rt3 - rt3(4)
+2001:db8::4/128 IP6 internal 30 rt4 - rt4(4)
+2001:db8::5/128 IP6 internal 30 rt2 - rt5(4)
+2001:db8::6/128 IP6 internal 40 rt3 - rt6(4)
+2001:db8::7/128 IP6 internal 40 rt4 - rt7(4)
+2001:db8::8/128 IP6 internal 40 rt2 - rt8(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::2/128 20 - rt2 implicit-null
+ 2001:db8::3/128 30 - rt3 implicit-null
+ 2001:db8::4/128 30 - rt4 implicit-null
+ 2001:db8::5/128 30 - rt2 16051
+ 2001:db8::6/128 40 - rt3 20061
+ 2001:db8::7/128 40 - rt4 16071
+ 2001:db8::8/128 40 - rt2 16081
+
+test# test isis topology 11 root rt1 spf
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt2 pseudo_TE-IS 20 rt3 - rt3(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+ rt3 -
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 40 - rt2 16060
+ - rt3 16060
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt2 pseudo_TE-IS 20 rt3 - rt3(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+2001:db8::6/128 IP6 internal 40 rt2 - rt6(4)
+ rt3 -
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::2/128 20 - rt2 implicit-null
+ 2001:db8::3/128 20 - rt3 implicit-null
+ 2001:db8::4/128 30 - rt2 16041
+ 2001:db8::5/128 30 - rt3 16051
+ 2001:db8::6/128 40 - rt2 16061
+ - rt3 16061
+
+test# test isis topology 12 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt2 - rt6(4)
+rt9 TE-IS 40 rt3 - rt7(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+rt10 TE-IS 50 rt2 - rt8(4)
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)
+10.0.255.9/32 IP TE 50 rt3 - rt9(4)
+10.0.255.10/32 IP TE 60 rt2 - rt10(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 40 - rt2 16060
+ 10.0.255.7/32 40 - rt3 16070
+ 10.0.255.8/32 50 - rt2 16080
+ 10.0.255.9/32 50 - rt3 16090
+ 10.0.255.10/32 60 - rt2 16100
+
+test# test isis topology 13 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+ rt3 - rt3(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+ rt6(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+ rt3 -
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ - rt3 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 30 - rt3 16060
+ 10.0.255.7/32 40 - rt3 16070
+
+test#
+test# test isis topology 4 root rt1 reverse-spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt2 - rt6(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 40 - rt2 16060
+ 10.0.255.7/32 40 - rt3 16070
+ 10.0.255.8/32 50 - rt2 16080
+
+test# test isis topology 11 root rt1 reverse-spf
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt2 pseudo_TE-IS 20 rt3 - rt3(4)
+rt4 TE-IS 20 rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt3 - rt4(4)
+ rt5(4)
+10.0.255.4/32 IP TE 30 rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 40 rt3 - rt6(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 40 - rt3 16060
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt2 pseudo_TE-IS 20 rt3 - rt3(4)
+rt4 TE-IS 20 rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 20 rt2(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt3 - rt4(4)
+ rt5(4)
+2001:db8::4/128 IP6 internal 30 rt4(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+2001:db8::6/128 IP6 internal 40 rt3 - rt6(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::3/128 20 - rt3 implicit-null
+ 2001:db8::5/128 30 - rt3 16051
+ 2001:db8::6/128 40 - rt3 16061
+
+test#
+test# test isis topology 1 root rt1 lfa system-id rt2
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+ rt3 -
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 40 - rt2 16060
+ - rt3 16060
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+2001:db8::6/128 IP6 internal 40 rt2 - rt6(4)
+ rt3 -
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::2/128 20 - rt2 implicit-null
+ 2001:db8::3/128 20 - rt3 implicit-null
+ 2001:db8::4/128 30 - rt2 16041
+ 2001:db8::5/128 30 - rt3 16051
+ 2001:db8::6/128 40 - rt2 16061
+ - rt3 16061
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 2 root rt4 lfa system-id rt1 pseudonode-id 1
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+10.0.255.4/32 IP internal 0 rt4(4)
+rt1 TE-IS 10 rt1 - rt4(4)
+rt5 TE-IS 10 rt5 - rt4(4)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt5 - rt5(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt2 TE-IS 25 rt1 - rt1(4)
+10.0.255.2/32 IP TE 35 rt1 - rt2(4)
+rt3 TE-IS 40 rt1 - rt1(4)
+10.0.255.3/32 IP TE 50 rt1 - rt3(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 20 - rt1 implicit-null
+ 10.0.255.2/32 35 - rt1 16020
+ 10.0.255.3/32 50 - rt1 16030
+ 10.0.255.4/32 0 - - -
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.2/32 50 - rt2 implicit-null
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+2001:db8::4/128 IP6 internal 0 rt4(4)
+rt1 TE-IS 10 rt1 - rt4(4)
+rt5 TE-IS 10 rt5 - rt4(4)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt5 - rt5(4)
+2001:db8::1/128 IP6 internal 20 rt1 - rt1(4)
+2001:db8::5/128 IP6 internal 20 rt5 - rt5(4)
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)
+rt2 TE-IS 25 rt1 - rt1(4)
+2001:db8::2/128 IP6 internal 35 rt1 - rt2(4)
+rt3 TE-IS 40 rt1 - rt1(4)
+2001:db8::3/128 IP6 internal 50 rt1 - rt3(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 20 - rt1 implicit-null
+ 2001:db8::2/128 35 - rt1 16021
+ 2001:db8::3/128 50 - rt1 16031
+ 2001:db8::4/128 0 - - -
+ 2001:db8::5/128 20 - rt5 implicit-null
+ 2001:db8::6/128 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::2/128 50 - rt2 implicit-null
+
+test# test isis topology 2 root rt4 lfa system-id rt6
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+10.0.255.4/32 IP internal 0 rt4(4)
+rt1 TE-IS 10 rt1 - rt4(4)
+rt5 TE-IS 10 rt5 - rt4(4)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt5 - rt5(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt2 TE-IS 25 rt1 - rt1(4)
+10.0.255.2/32 IP TE 35 rt1 - rt2(4)
+rt3 TE-IS 40 rt1 - rt1(4)
+10.0.255.3/32 IP TE 50 rt1 - rt3(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 20 - rt1 implicit-null
+ 10.0.255.2/32 35 - rt1 16020
+ 10.0.255.3/32 50 - rt1 16030
+ 10.0.255.4/32 0 - - -
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.6/32 30 - rt5 16060
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+2001:db8::4/128 IP6 internal 0 rt4(4)
+rt1 TE-IS 10 rt1 - rt4(4)
+rt5 TE-IS 10 rt5 - rt4(4)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt5 - rt5(4)
+2001:db8::1/128 IP6 internal 20 rt1 - rt1(4)
+2001:db8::5/128 IP6 internal 20 rt5 - rt5(4)
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)
+rt2 TE-IS 25 rt1 - rt1(4)
+2001:db8::2/128 IP6 internal 35 rt1 - rt2(4)
+rt3 TE-IS 40 rt1 - rt1(4)
+2001:db8::3/128 IP6 internal 50 rt1 - rt3(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 20 - rt1 implicit-null
+ 2001:db8::2/128 35 - rt1 16021
+ 2001:db8::3/128 50 - rt1 16031
+ 2001:db8::4/128 0 - - -
+ 2001:db8::5/128 20 - rt5 implicit-null
+ 2001:db8::6/128 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::6/128 30 - rt5 16061
+
+test# test isis topology 3 root rt1 lfa system-id rt2
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt5 TE-IS 30 rt2 - rt4(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 40 rt2 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 40 - rt2 16050
+ 10.0.255.6/32 40 - rt2 16060
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 30 - rt3 16020
+ 10.0.255.4/32 40 - rt3 16040
+ 10.0.255.5/32 50 - rt3 16050
+ 10.0.255.6/32 50 - rt3 16060
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 3 root rt1 lfa system-id rt3
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt5 TE-IS 30 rt2 - rt4(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 40 rt2 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 40 - rt2 16050
+ 10.0.255.6/32 40 - rt2 16060
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.3/32 30 - rt2 16030
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 7 root rt1 lfa system-id rt4
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt5 TE-IS 20 rt4 - rt4(4)
+rt7 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+rt2 TE-IS 30 rt4 - rt5(4)
+rt6 TE-IS 30 rt4 - rt5(4)
+rt8 TE-IS 30 rt4 - rt5(4)
+ rt7(4)
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)
+10.0.255.7/32 IP TE 30 rt4 - rt7(4)
+rt10 TE-IS 40 rt4 - rt7(4)
+rt3 TE-IS 40 rt4 - rt2(4)
+ rt6(4)
+rt9 TE-IS 40 rt4 - rt8(4)
+rt11 TE-IS 40 rt4 - rt8(4)
+10.0.255.2/32 IP TE 40 rt4 - rt2(4)
+10.0.255.6/32 IP TE 40 rt4 - rt6(4)
+10.0.255.8/32 IP TE 40 rt4 - rt8(4)
+rt12 TE-IS 50 rt4 - rt9(4)
+ rt11(4)
+10.0.255.10/32 IP TE 50 rt4 - rt10(4)
+10.0.255.3/32 IP TE 50 rt4 - rt3(4)
+10.0.255.9/32 IP TE 50 rt4 - rt9(4)
+10.0.255.11/32 IP TE 50 rt4 - rt11(4)
+10.0.255.12/32 IP TE 60 rt4 - rt12(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 40 - rt4 16020
+ 10.0.255.3/32 50 - rt4 16030
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 30 - rt4 16050
+ 10.0.255.6/32 40 - rt4 16060
+ 10.0.255.7/32 30 - rt4 16070
+ 10.0.255.8/32 40 - rt4 16080
+ 10.0.255.9/32 50 - rt4 16090
+ 10.0.255.10/32 50 - rt4 16100
+ 10.0.255.11/32 50 - rt4 16110
+ 10.0.255.12/32 60 - rt4 16120
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.2/32 50 - rt2 implicit-null
+ 10.0.255.3/32 60 - rt2 16030
+ 10.0.255.4/32 70 - rt2 16040
+ 10.0.255.5/32 60 - rt2 16050
+ 10.0.255.6/32 70 - rt2 16060
+ 10.0.255.7/32 80 - rt2 16070
+ 10.0.255.8/32 70 - rt2 16080
+ 10.0.255.9/32 80 - rt2 16090
+ 10.0.255.10/32 90 - rt2 16100
+ 10.0.255.11/32 80 - rt2 16110
+ 10.0.255.12/32 90 - rt2 16120
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+rt4 TE-IS 10 rt4 - rt1(4)
+rt2 TE-IS 40 rt2 - rt1(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 7 root rt7 lfa system-id rt8
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt7
+10.0.255.7/32 IP internal 0 rt7(4)
+rt4 TE-IS 10 rt4 - rt7(4)
+rt8 TE-IS 10 rt8 - rt7(4)
+rt10 TE-IS 20 rt10 - rt7(4)
+rt1 TE-IS 20 rt4 - rt4(4)
+rt5 TE-IS 20 rt4 - rt4(4)
+ rt8 - rt8(4)
+rt9 TE-IS 20 rt8 - rt8(4)
+rt11 TE-IS 20 rt8 - rt8(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.8/32 IP TE 20 rt8 - rt8(4)
+rt2 TE-IS 30 rt4 - rt5(4)
+ rt8 -
+rt6 TE-IS 30 rt4 - rt5(4)
+ rt8 -
+rt12 TE-IS 30 rt8 - rt9(4)
+ rt11(4)
+10.0.255.10/32 IP TE 30 rt10 - rt10(4)
+10.0.255.1/32 IP TE 30 rt4 - rt1(4)
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)
+ rt8 -
+10.0.255.9/32 IP TE 30 rt8 - rt9(4)
+10.0.255.11/32 IP TE 30 rt8 - rt11(4)
+rt3 TE-IS 40 rt4 - rt2(4)
+ rt8 - rt6(4)
+10.0.255.2/32 IP TE 40 rt4 - rt2(4)
+ rt8 -
+10.0.255.6/32 IP TE 40 rt4 - rt6(4)
+ rt8 -
+10.0.255.12/32 IP TE 40 rt8 - rt12(4)
+10.0.255.3/32 IP TE 50 rt4 - rt3(4)
+ rt8 -
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 30 - rt4 16010
+ 10.0.255.2/32 40 - rt4 16020
+ - rt8 16020
+ 10.0.255.3/32 50 - rt4 16030
+ - rt8 16030
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 30 - rt4 16050
+ - rt8 16050
+ 10.0.255.6/32 40 - rt4 16060
+ - rt8 16060
+ 10.0.255.7/32 0 - - -
+ 10.0.255.8/32 20 - rt8 implicit-null
+ 10.0.255.9/32 30 - rt8 16090
+ 10.0.255.10/32 30 - rt10 implicit-null
+ 10.0.255.11/32 30 - rt8 16110
+ 10.0.255.12/32 40 - rt8 16120
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------
+ 10.0.255.8/32 50 - rt10 16080
+ 10.0.255.9/32 60 - rt10 16090
+ 10.0.255.11/32 40 - rt10 16110
+ 10.0.255.12/32 50 - rt10 16120
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt7
+rt4 TE-IS 10 rt4 - rt7(4)
+rt8 TE-IS 10 rt8 - rt7(4)
+rt10 TE-IS 20 rt10 - rt7(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 7 root rt8 lfa system-id rt11
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt8
+10.0.255.8/32 IP internal 0 rt8(4)
+rt5 TE-IS 10 rt5 - rt8(4)
+rt7 TE-IS 10 rt7 - rt8(4)
+rt9 TE-IS 10 rt9 - rt8(4)
+rt11 TE-IS 10 rt11 - rt8(4)
+rt2 TE-IS 20 rt5 - rt5(4)
+rt4 TE-IS 20 rt5 - rt5(4)
+ rt7 - rt7(4)
+rt6 TE-IS 20 rt5 - rt5(4)
+rt12 TE-IS 20 rt9 - rt9(4)
+ rt11 - rt11(4)
+rt10 TE-IS 20 rt11 - rt11(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+10.0.255.7/32 IP TE 20 rt7 - rt7(4)
+10.0.255.9/32 IP TE 20 rt9 - rt9(4)
+10.0.255.11/32 IP TE 20 rt11 - rt11(4)
+rt3 TE-IS 30 rt5 - rt2(4)
+ rt6(4)
+rt1 TE-IS 30 rt5 - rt4(4)
+ rt7 -
+10.0.255.2/32 IP TE 30 rt5 - rt2(4)
+10.0.255.4/32 IP TE 30 rt5 - rt4(4)
+ rt7 -
+10.0.255.6/32 IP TE 30 rt5 - rt6(4)
+10.0.255.12/32 IP TE 30 rt9 - rt12(4)
+ rt11 -
+10.0.255.10/32 IP TE 30 rt11 - rt10(4)
+10.0.255.3/32 IP TE 40 rt5 - rt3(4)
+10.0.255.1/32 IP TE 40 rt5 - rt1(4)
+ rt7 -
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 40 - rt5 16010
+ - rt7 16010
+ 10.0.255.2/32 30 - rt5 16020
+ 10.0.255.3/32 40 - rt5 16030
+ 10.0.255.4/32 30 - rt5 16040
+ - rt7 16040
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 30 - rt5 16060
+ 10.0.255.7/32 20 - rt7 implicit-null
+ 10.0.255.8/32 0 - - -
+ 10.0.255.9/32 20 - rt9 implicit-null
+ 10.0.255.10/32 30 - rt11 16100
+ 10.0.255.11/32 20 - rt11 implicit-null
+ 10.0.255.12/32 30 - rt9 16120
+ - rt11 16120
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------
+ 10.0.255.10/32 40 - rt7 16100
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt8
+rt5 TE-IS 10 rt5 - rt8(4)
+rt7 TE-IS 10 rt7 - rt8(4)
+rt9 TE-IS 10 rt9 - rt8(4)
+rt11 TE-IS 10 rt11 - rt8(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 9 root rt3 lfa system-id rt1
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt3
+10.0.255.3/32 IP internal 0 rt3(4)
+rt1 TE-IS 10 rt1 - rt3(4)
+rt2 TE-IS 20 rt1 - rt1(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+rt4 TE-IS 30 rt1 - rt2(4)
+10.0.255.2/32 IP TE 30 rt1 - rt2(4)
+rt5 TE-IS 40 rt1 - rt4(4)
+10.0.255.4/32 IP TE 40 rt1 - rt4(4)
+rt9 TE-IS 50 rt1 - rt5(4)
+10.0.255.5/32 IP TE 50 rt1 - rt5(4)
+rt6 TE-IS 60 rt1 - rt4(4)
+ rt9(4)
+rt7 TE-IS 60 rt1 - rt4(4)
+ rt9(4)
+rt8 TE-IS 60 rt1 - rt4(4)
+ rt9(4)
+10.0.255.9/32 IP TE 60 rt1 - rt9(4)
+10.0.255.6/32 IP TE 70 rt1 - rt6(4)
+10.0.255.7/32 IP TE 70 rt1 - rt7(4)
+10.0.255.8/32 IP TE 70 rt1 - rt8(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 20 - rt1 implicit-null
+ 10.0.255.2/32 30 - rt1 16020
+ 10.0.255.3/32 0 - - -
+ 10.0.255.4/32 40 - rt1 16040
+ 10.0.255.5/32 50 - rt1 16050
+ 10.0.255.6/32 70 - rt1 16060
+ 10.0.255.7/32 70 - rt1 16070
+ 10.0.255.8/32 70 - rt1 16080
+ 10.0.255.9/32 60 - rt1 16090
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 130 - rt4 16010
+ 10.0.255.2/32 120 - rt4 16020
+ 10.0.255.4/32 110 - rt4 implicit-null
+ 10.0.255.5/32 120 - rt4 16050
+ 10.0.255.6/32 140 - rt4 16060
+ 10.0.255.7/32 140 - rt4 16070
+ 10.0.255.8/32 140 - rt4 16080
+ 10.0.255.9/32 130 - rt4 16090
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt3
+2001:db8::3/128 IP6 internal 0 rt3(4)
+rt1 TE-IS 10 rt1 - rt3(4)
+rt2 TE-IS 20 rt1 - rt1(4)
+2001:db8::1/128 IP6 internal 20 rt1 - rt1(4)
+rt4 TE-IS 30 rt1 - rt2(4)
+2001:db8::2/128 IP6 internal 30 rt1 - rt2(4)
+rt5 TE-IS 40 rt1 - rt4(4)
+2001:db8::4/128 IP6 internal 40 rt1 - rt4(4)
+rt9 TE-IS 50 rt1 - rt5(4)
+2001:db8::5/128 IP6 internal 50 rt1 - rt5(4)
+rt6 TE-IS 60 rt1 - rt4(4)
+ rt9(4)
+rt7 TE-IS 60 rt1 - rt4(4)
+ rt9(4)
+rt8 TE-IS 60 rt1 - rt4(4)
+ rt9(4)
+2001:db8::9/128 IP6 internal 60 rt1 - rt9(4)
+2001:db8::6/128 IP6 internal 70 rt1 - rt6(4)
+2001:db8::7/128 IP6 internal 70 rt1 - rt7(4)
+2001:db8::8/128 IP6 internal 70 rt1 - rt8(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 20 - rt1 implicit-null
+ 2001:db8::2/128 30 - rt1 16021
+ 2001:db8::3/128 0 - - -
+ 2001:db8::4/128 40 - rt1 16041
+ 2001:db8::5/128 50 - rt1 16051
+ 2001:db8::6/128 70 - rt1 16061
+ 2001:db8::7/128 70 - rt1 16071
+ 2001:db8::8/128 70 - rt1 16081
+ 2001:db8::9/128 60 - rt1 16091
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 130 - rt4 16011
+ 2001:db8::2/128 120 - rt4 16021
+ 2001:db8::4/128 110 - rt4 implicit-null
+ 2001:db8::5/128 120 - rt4 16051
+ 2001:db8::6/128 140 - rt4 16061
+ 2001:db8::7/128 140 - rt4 16071
+ 2001:db8::8/128 140 - rt4 16081
+ 2001:db8::9/128 130 - rt4 16091
+
+test# test isis topology 10 root rt8 lfa system-id rt5
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt8
+10.0.255.8/32 IP internal 0 rt8(4)
+rt5 TE-IS 10 rt5 - rt8(4)
+rt2 TE-IS 20 rt5 - rt5(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+rt1 TE-IS 30 rt5 - rt2(4)
+10.0.255.2/32 IP TE 30 rt5 - rt2(4)
+10.0.255.1/32 IP TE 40 rt5 - rt1(4)
+rt6 TE-IS 50 rt6 - rt8(4)
+rt7 TE-IS 50 rt7 - rt8(4)
+rt3 TE-IS 50 rt5 - rt1(4)
+rt4 TE-IS 50 rt5 - rt1(4)
+10.0.255.6/32 IP TE 60 rt6 - rt6(4)
+10.0.255.7/32 IP TE 60 rt7 - rt7(4)
+10.0.255.3/32 IP TE 60 rt5 - rt3(4)
+10.0.255.4/32 IP TE 60 rt5 - rt4(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 40 - rt5 16010
+ 10.0.255.2/32 30 - rt5 16020
+ 10.0.255.3/32 60 - rt5 16030
+ 10.0.255.4/32 60 - rt5 16040
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 60 - rt6 implicit-null
+ 10.0.255.7/32 60 - rt7 implicit-null
+ 10.0.255.8/32 0 - - -
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.1/32 90 - rt6 16010
+ - rt7 16010
+ 10.0.255.2/32 100 - rt6 16020
+ - rt7 16020
+ 10.0.255.3/32 70 - rt6 16030
+ - rt7 16030
+ 10.0.255.4/32 70 - rt6 16040
+ - rt7 16040
+ 10.0.255.5/32 110 - rt6 16050
+ - rt7 16050
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt8
+2001:db8::8/128 IP6 internal 0 rt8(4)
+rt5 TE-IS 10 rt5 - rt8(4)
+rt2 TE-IS 20 rt5 - rt5(4)
+2001:db8::5/128 IP6 internal 20 rt5 - rt5(4)
+rt1 TE-IS 30 rt5 - rt2(4)
+2001:db8::2/128 IP6 internal 30 rt5 - rt2(4)
+2001:db8::1/128 IP6 internal 40 rt5 - rt1(4)
+rt6 TE-IS 50 rt6 - rt8(4)
+rt7 TE-IS 50 rt7 - rt8(4)
+rt3 TE-IS 50 rt5 - rt1(4)
+rt4 TE-IS 50 rt5 - rt1(4)
+2001:db8::6/128 IP6 internal 60 rt6 - rt6(4)
+2001:db8::7/128 IP6 internal 60 rt7 - rt7(4)
+2001:db8::3/128 IP6 internal 60 rt5 - rt3(4)
+2001:db8::4/128 IP6 internal 60 rt5 - rt4(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 40 - rt5 16011
+ 2001:db8::2/128 30 - rt5 16021
+ 2001:db8::3/128 60 - rt5 16031
+ 2001:db8::4/128 60 - rt5 16041
+ 2001:db8::5/128 20 - rt5 implicit-null
+ 2001:db8::6/128 60 - rt6 implicit-null
+ 2001:db8::7/128 60 - rt7 implicit-null
+ 2001:db8::8/128 0 - - -
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::1/128 90 - rt6 16011
+ - rt7 16011
+ 2001:db8::2/128 100 - rt6 16021
+ - rt7 16021
+ 2001:db8::3/128 70 - rt6 16031
+ - rt7 16031
+ 2001:db8::4/128 70 - rt6 16041
+ - rt7 16041
+ 2001:db8::5/128 110 - rt6 16051
+ - rt7 16051
+
+test# test isis topology 11 root rt3 lfa system-id rt5
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt3
+10.0.255.3/32 IP internal 0 rt3(4)
+rt1 TE-IS 10 rt1 - rt3(4)
+rt2 TE-IS 10 rt2 - rt3(4)
+rt5 TE-IS 10 rt5 - rt3(4)
+rt2 pseudo_TE-IS 20 rt1 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+ rt5 - rt5(4)
+rt6 TE-IS 20 rt5 - rt5(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+ rt5 -
+10.0.255.6/32 IP TE 30 rt5 - rt6(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 20 - rt1 implicit-null
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 0 - - -
+ 10.0.255.4/32 30 - rt2 16040
+ - rt5 16040
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 30 - rt5 16060
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.5/32 40 - rt2 16050
+ 10.0.255.6/32 40 - rt2 16060
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt3
+2001:db8::3/128 IP6 internal 0 rt3(4)
+rt1 TE-IS 10 rt1 - rt3(4)
+rt2 TE-IS 10 rt2 - rt3(4)
+rt5 TE-IS 10 rt5 - rt3(4)
+rt2 pseudo_TE-IS 20 rt1 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+ rt5 - rt5(4)
+rt6 TE-IS 20 rt5 - rt5(4)
+2001:db8::1/128 IP6 internal 20 rt1 - rt1(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::5/128 IP6 internal 20 rt5 - rt5(4)
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)
+ rt5 -
+2001:db8::6/128 IP6 internal 30 rt5 - rt6(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 20 - rt1 implicit-null
+ 2001:db8::2/128 20 - rt2 implicit-null
+ 2001:db8::3/128 0 - - -
+ 2001:db8::4/128 30 - rt2 16041
+ - rt5 16041
+ 2001:db8::5/128 20 - rt5 implicit-null
+ 2001:db8::6/128 30 - rt5 16061
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::5/128 40 - rt2 16051
+ 2001:db8::6/128 40 - rt2 16061
+
+test# test isis topology 13 root rt4 lfa system-id rt3
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+10.0.255.4/32 IP internal 0 rt4(4)
+rt2 TE-IS 10 rt2 - rt4(4)
+rt3 TE-IS 10 rt3 - rt4(4)
+rt1 TE-IS 20 rt2 - rt2(4)
+ rt3 - rt3(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+ rt6(4)
+10.0.255.1/32 IP TE 30 rt2 - rt1(4)
+ rt3 -
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 30 - rt2 16010
+ - rt3 16010
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 0 - - -
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 30 - rt3 16060
+ 10.0.255.7/32 40 - rt3 16070
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.3/32 120 - rt5 16030
+ 10.0.255.5/32 110 - rt5 implicit-null
+ 10.0.255.6/32 130 - rt5 16060
+ 10.0.255.7/32 120 - rt5 16070
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+rt2 TE-IS 10 rt2 - rt4(4)
+rt3 TE-IS 10 rt3 - rt4(4)
+rt5 TE-IS 100 rt5 - rt4(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 14 root rt1 lfa system-id rt1 pseudonode-id 1
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt1
+rt5 TE-IS 20 rt4 - rt4(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 20 - rt4 -
+ 10.0.255.5/32 30 - rt4 -
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt1
+rt5 TE-IS 20 rt4 - rt4(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::5/128 IP6 internal 30 rt4 - rt5(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::2/128 20 - rt2 -
+ 2001:db8::3/128 20 - rt3 -
+ 2001:db8::4/128 20 - rt4 -
+ 2001:db8::5/128 30 - rt4 -
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 14 root rt1 lfa system-id rt2
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt1
+rt5 TE-IS 20 rt4 - rt4(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 20 - rt4 -
+ 10.0.255.5/32 30 - rt4 -
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 30 - rt3 -
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt1
+rt5 TE-IS 20 rt4 - rt4(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::5/128 IP6 internal 30 rt4 - rt5(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::2/128 20 - rt2 -
+ 2001:db8::3/128 20 - rt3 -
+ 2001:db8::4/128 20 - rt4 -
+ 2001:db8::5/128 30 - rt4 -
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::2/128 30 - rt3 -
+
+test# test isis topology 14 root rt5 lfa system-id rt4
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt1 pseudo_TE-IS 20 rt4 - rt4(4)
+rt1 TE-IS 20 rt4 - rt1(2)
+rt3 TE-IS 20 rt4 - rt1(2)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+rt2 TE-IS 30 rt4 - rt1(4)
+ rt3(4)
+10.0.255.1/32 IP TE 30 rt4 - rt1(4)
+10.0.255.3/32 IP TE 30 rt4 - rt3(4)
+10.0.255.2/32 IP TE 40 rt4 - rt2(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.1/32 30 - rt4 -
+ 10.0.255.2/32 40 - rt4 -
+ 10.0.255.3/32 30 - rt4 -
+ 10.0.255.4/32 20 - rt4 -
+ 10.0.255.5/32 0 - - -
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.1/32 70 - rt3 -
+ 10.0.255.2/32 70 - rt3 -
+ 10.0.255.3/32 60 - rt3 -
+ 10.0.255.4/32 70 - rt3 -
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+2001:db8::5/128 IP6 internal 0 rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt1 pseudo_TE-IS 20 rt4 - rt4(4)
+rt1 TE-IS 20 rt4 - rt1(2)
+rt3 TE-IS 20 rt4 - rt1(2)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+rt2 TE-IS 30 rt4 - rt1(4)
+ rt3(4)
+2001:db8::1/128 IP6 internal 30 rt4 - rt1(4)
+2001:db8::3/128 IP6 internal 30 rt4 - rt3(4)
+2001:db8::2/128 IP6 internal 40 rt4 - rt2(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::1/128 30 - rt4 -
+ 2001:db8::2/128 40 - rt4 -
+ 2001:db8::3/128 30 - rt4 -
+ 2001:db8::4/128 20 - rt4 -
+ 2001:db8::5/128 0 - - -
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::1/128 70 - rt3 -
+ 2001:db8::2/128 70 - rt3 -
+ 2001:db8::3/128 60 - rt3 -
+ 2001:db8::4/128 70 - rt3 -
+
+test#
+test# test isis topology 1 root rt1 remote-lfa system-id rt2
+P-space (self):
+ rt3
+ rt5
+
+P-space (rt3):
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt2
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt3 - rt5(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt4 TE-IS 40 rt3 - rt6(4)
+10.0.255.6/32 IP TE 40 rt3 - rt6(4)
+rt2 TE-IS 50 rt3 - rt4(4)
+10.0.255.4/32 IP TE 50 rt3 - rt4(4)
+10.0.255.2/32 IP TE 60 rt3 - rt2(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+ rt3 -
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 40 - rt2 16060
+ - rt3 16060
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.2/32 60 - rt3 50600/16020
+ 10.0.255.4/32 50 - rt3 50600/16040
+
+P-space (self):
+ rt3
+ rt5
+
+P-space (rt3):
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt2
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt3 - rt5(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+rt4 TE-IS 40 rt3 - rt6(4)
+2001:db8::6/128 IP6 internal 40 rt3 - rt6(4)
+rt2 TE-IS 50 rt3 - rt4(4)
+2001:db8::4/128 IP6 internal 50 rt3 - rt4(4)
+2001:db8::2/128 IP6 internal 60 rt3 - rt2(4)
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+2001:db8::6/128 IP6 internal 40 rt2 - rt6(4)
+ rt3 -
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::2/128 20 - rt2 implicit-null
+ 2001:db8::3/128 20 - rt3 implicit-null
+ 2001:db8::4/128 30 - rt2 16041
+ 2001:db8::5/128 30 - rt3 16051
+ 2001:db8::6/128 40 - rt2 16061
+ - rt3 16061
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 2001:db8::2/128 60 - rt3 50600/16021
+ 2001:db8::4/128 50 - rt3 50600/16041
+
+test# test isis topology 2 root rt5 remote-lfa system-id rt1 pseudonode-id 1
+P-space (self):
+ rt6
+
+P-space (rt3):
+ rt1
+ rt2
+ rt3
+ rt4
+
+P-space (rt6):
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt4 TE-IS 20 rt6 - rt6(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt1 pseudo_TE-IS 30 rt6 - rt4(4)
+rt1 TE-IS 30 rt6 - rt1(2)
+10.0.255.4/32 IP TE 30 rt6 - rt4(4)
+rt3 TE-IS 40 rt3 - rt5(4)
+10.0.255.1/32 IP TE 40 rt6 - rt1(4)
+rt2 TE-IS 45 rt6 - rt1(4)
+10.0.255.3/32 IP TE 50 rt3 - rt3(4)
+10.0.255.2/32 IP TE 55 rt6 - rt2(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt1 TE-IS 10 rt1 - rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt4 - rt4(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt2 TE-IS 25 rt1 - rt1(4)
+10.0.255.2/32 IP TE 35 rt1 - rt2(4)
+rt3 TE-IS 40 rt3 - rt5(4)
+ rt1 - rt1(4)
+10.0.255.3/32 IP TE 50 rt3 - rt3(4)
+ rt1 -
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 20 - rt1 implicit-null
+ 10.0.255.2/32 35 - rt1 16020
+ 10.0.255.3/32 50 - rt3 implicit-null
+ - rt1 implicit-null
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 0 - - -
+ 10.0.255.6/32 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.1/32 40 - rt6 50400/16010
+ 10.0.255.2/32 55 - rt6 50400/16020
+ 10.0.255.4/32 30 - rt6 50400/16040
+
+P-space (self):
+ rt6
+
+P-space (rt3):
+ rt1
+ rt2
+ rt3
+ rt4
+
+P-space (rt6):
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+2001:db8::5/128 IP6 internal 0 rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt4 TE-IS 20 rt6 - rt6(4)
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)
+rt1 pseudo_TE-IS 30 rt6 - rt4(4)
+rt1 TE-IS 30 rt6 - rt1(2)
+2001:db8::4/128 IP6 internal 30 rt6 - rt4(4)
+rt3 TE-IS 40 rt3 - rt5(4)
+2001:db8::1/128 IP6 internal 40 rt6 - rt1(4)
+rt2 TE-IS 45 rt6 - rt1(4)
+2001:db8::3/128 IP6 internal 50 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 55 rt6 - rt2(4)
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+2001:db8::5/128 IP6 internal 0 rt5(4)
+rt1 TE-IS 10 rt1 - rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt4 - rt4(4)
+2001:db8::1/128 IP6 internal 20 rt1 - rt1(4)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)
+rt2 TE-IS 25 rt1 - rt1(4)
+2001:db8::2/128 IP6 internal 35 rt1 - rt2(4)
+rt3 TE-IS 40 rt3 - rt5(4)
+ rt1 - rt1(4)
+2001:db8::3/128 IP6 internal 50 rt3 - rt3(4)
+ rt1 -
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 20 - rt1 implicit-null
+ 2001:db8::2/128 35 - rt1 16021
+ 2001:db8::3/128 50 - rt3 implicit-null
+ - rt1 implicit-null
+ 2001:db8::4/128 20 - rt4 implicit-null
+ 2001:db8::5/128 0 - - -
+ 2001:db8::6/128 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 2001:db8::1/128 40 - rt6 50400/16011
+ 2001:db8::2/128 55 - rt6 50400/16021
+ 2001:db8::4/128 30 - rt6 50400/16041
+
+test# test isis topology 3 root rt5 remote-lfa system-id rt4 ipv4-only
+P-space (self):
+ rt6
+
+P-space (rt3):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+P-space (rt6):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt4 TE-IS 20 rt6 - rt6(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt3 - rt5(4)
+rt2 TE-IS 30 rt6 - rt4(4)
+10.0.255.4/32 IP TE 30 rt6 - rt4(4)
+rt1 TE-IS 40 rt3 - rt3(4)
+ rt6 - rt2(4)
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)
+10.0.255.2/32 IP TE 40 rt6 - rt2(4)
+10.0.255.1/32 IP TE 50 rt3 - rt1(4)
+ rt6 -
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt2 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt3 - rt5(4)
+ rt4 - rt2(4)
+rt1 TE-IS 30 rt4 - rt2(4)
+10.0.255.2/32 IP TE 30 rt4 - rt2(4)
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)
+ rt4 -
+10.0.255.1/32 IP TE 40 rt4 - rt1(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 40 - rt4 16010
+ 10.0.255.2/32 30 - rt4 16020
+ 10.0.255.3/32 40 - rt3 implicit-null
+ - rt4 implicit-null
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 0 - - -
+ 10.0.255.6/32 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.1/32 50 - rt3 16010
+ - rt6 16010
+ 10.0.255.2/32 40 - rt3 16020
+ - rt6 16020
+ 10.0.255.4/32 30 - rt3 16040
+ - rt6 16040
+
+test# test isis topology 3 root rt5 remote-lfa system-id rt3 ipv4-only
+P-space (self):
+ rt1
+ rt2
+ rt4
+ rt6
+
+P-space (rt4):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+P-space (rt6):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt2 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt1 TE-IS 30 rt4 - rt2(4)
+rt3 TE-IS 30 rt4 - rt2(4)
+10.0.255.2/32 IP TE 30 rt4 - rt2(4)
+10.0.255.1/32 IP TE 40 rt4 - rt1(4)
+10.0.255.3/32 IP TE 40 rt4 - rt3(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt2 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt3 - rt5(4)
+ rt4 - rt2(4)
+rt1 TE-IS 30 rt4 - rt2(4)
+10.0.255.2/32 IP TE 30 rt4 - rt2(4)
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)
+ rt4 -
+10.0.255.1/32 IP TE 40 rt4 - rt1(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 40 - rt4 16010
+ 10.0.255.2/32 30 - rt4 16020
+ 10.0.255.3/32 40 - rt3 implicit-null
+ - rt4 implicit-null
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 0 - - -
+ 10.0.255.6/32 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+test# test isis topology 5 root rt1 remote-lfa system-id rt2 ipv4-only
+P-space (self):
+ rt3
+ rt5
+ rt7
+
+P-space (rt3):
+ rt3
+ rt5
+ rt7
+ rt8
+
+Q-space:
+ rt2
+ rt4
+ rt6
+ rt8
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt3 - rt7(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+rt6 TE-IS 50 rt3 - rt8(4)
+10.0.255.8/32 IP TE 50 rt3 - rt8(4)
+rt4 TE-IS 60 rt3 - rt6(4)
+10.0.255.6/32 IP TE 60 rt3 - rt6(4)
+rt2 TE-IS 70 rt3 - rt4(4)
+10.0.255.4/32 IP TE 70 rt3 - rt4(4)
+10.0.255.2/32 IP TE 80 rt3 - rt2(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt2 - rt6(4)
+ rt3 - rt7(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)
+ rt3 -
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 40 - rt2 16060
+ 10.0.255.7/32 40 - rt3 16070
+ 10.0.255.8/32 50 - rt2 16080
+ - rt3 16080
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.2/32 80 - rt3 50800/16020
+ 10.0.255.4/32 70 - rt3 50800/16040
+ 10.0.255.6/32 60 - rt3 50800/16060
+
+test# test isis topology 6 root rt4 remote-lfa system-id rt3 ipv4-only
+P-space (self):
+ rt2
+ rt5
+ rt6
+ rt7
+ rt8
+
+P-space (rt2):
+ rt1
+ rt2
+
+P-space (rt6):
+ rt5
+ rt6
+ rt7
+ rt8
+
+Q-space:
+ rt1
+ rt3
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+10.0.255.4/32 IP internal 0 rt4(4)
+rt2 TE-IS 10 rt2 - rt4(4)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt6 - rt6(4)
+rt8 TE-IS 20 rt6 - rt6(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt2 - rt1(4)
+rt7 TE-IS 30 rt6 - rt5(4)
+ rt8(4)
+10.0.255.1/32 IP TE 30 rt2 - rt1(4)
+10.0.255.5/32 IP TE 30 rt6 - rt5(4)
+10.0.255.8/32 IP TE 30 rt6 - rt8(4)
+10.0.255.3/32 IP TE 40 rt2 - rt3(4)
+10.0.255.7/32 IP TE 40 rt6 - rt7(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+10.0.255.4/32 IP internal 0 rt4(4)
+rt2 TE-IS 10 rt2 - rt4(4)
+rt3 TE-IS 10 rt3 - rt4(4)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 TE-IS 20 rt2 - rt2(4)
+ rt3 - rt3(4)
+rt5 TE-IS 20 rt6 - rt6(4)
+rt8 TE-IS 20 rt6 - rt6(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt7 TE-IS 30 rt6 - rt5(4)
+ rt8(4)
+10.0.255.1/32 IP TE 30 rt2 - rt1(4)
+ rt3 -
+10.0.255.5/32 IP TE 30 rt6 - rt5(4)
+10.0.255.8/32 IP TE 30 rt6 - rt8(4)
+10.0.255.7/32 IP TE 40 rt6 - rt7(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 30 - rt2 16010
+ - rt3 16010
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 0 - - -
+ 10.0.255.5/32 30 - rt6 16050
+ 10.0.255.6/32 20 - rt6 implicit-null
+ 10.0.255.7/32 40 - rt6 16070
+ 10.0.255.8/32 30 - rt6 16080
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.3/32 40 - rt2 50100/16030
+
+test# test isis topology 7 root rt11 remote-lfa system-id rt8 ipv4-only
+P-space (self):
+ rt10
+ rt12
+
+P-space (rt10):
+ rt1
+ rt4
+ rt7
+ rt10
+
+P-space (rt12):
+ rt9
+ rt12
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt5
+ rt6
+ rt7
+ rt8
+ rt9
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt11
+10.0.255.11/32 IP internal 0 rt11(4)
+rt10 TE-IS 10 rt10 - rt11(4)
+rt12 TE-IS 10 rt12 - rt11(4)
+rt9 TE-IS 20 rt12 - rt12(4)
+10.0.255.10/32 IP TE 20 rt10 - rt10(4)
+10.0.255.12/32 IP TE 20 rt12 - rt12(4)
+rt7 TE-IS 30 rt10 - rt10(4)
+rt8 TE-IS 30 rt12 - rt9(4)
+10.0.255.9/32 IP TE 30 rt12 - rt9(4)
+rt4 TE-IS 40 rt10 - rt7(4)
+rt5 TE-IS 40 rt12 - rt8(4)
+10.0.255.7/32 IP TE 40 rt10 - rt7(4)
+10.0.255.8/32 IP TE 40 rt12 - rt8(4)
+rt6 TE-IS 50 rt12 - rt9(4)
+ rt5(4)
+rt1 TE-IS 50 rt10 - rt4(4)
+rt2 TE-IS 50 rt12 - rt5(4)
+10.0.255.4/32 IP TE 50 rt10 - rt4(4)
+10.0.255.5/32 IP TE 50 rt12 - rt5(4)
+rt3 TE-IS 60 rt12 - rt6(4)
+ rt2(4)
+10.0.255.6/32 IP TE 60 rt12 - rt6(4)
+10.0.255.1/32 IP TE 60 rt10 - rt1(4)
+10.0.255.2/32 IP TE 60 rt12 - rt2(4)
+10.0.255.3/32 IP TE 70 rt12 - rt3(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt11
+10.0.255.11/32 IP internal 0 rt11(4)
+rt8 TE-IS 10 rt8 - rt11(4)
+rt10 TE-IS 10 rt10 - rt11(4)
+rt12 TE-IS 10 rt12 - rt11(4)
+rt5 TE-IS 20 rt8 - rt8(4)
+rt7 TE-IS 20 rt8 - rt8(4)
+rt9 TE-IS 20 rt8 - rt8(4)
+ rt12 - rt12(4)
+10.0.255.8/32 IP TE 20 rt8 - rt8(4)
+10.0.255.10/32 IP TE 20 rt10 - rt10(4)
+10.0.255.12/32 IP TE 20 rt12 - rt12(4)
+rt2 TE-IS 30 rt8 - rt5(4)
+rt4 TE-IS 30 rt8 - rt5(4)
+ rt7(4)
+rt6 TE-IS 30 rt8 - rt5(4)
+10.0.255.5/32 IP TE 30 rt8 - rt5(4)
+10.0.255.7/32 IP TE 30 rt8 - rt7(4)
+10.0.255.9/32 IP TE 30 rt8 - rt9(4)
+ rt12 -
+rt3 TE-IS 40 rt8 - rt2(4)
+ rt6(4)
+rt1 TE-IS 40 rt8 - rt4(4)
+10.0.255.2/32 IP TE 40 rt8 - rt2(4)
+10.0.255.4/32 IP TE 40 rt8 - rt4(4)
+10.0.255.6/32 IP TE 40 rt8 - rt6(4)
+10.0.255.3/32 IP TE 50 rt8 - rt3(4)
+10.0.255.1/32 IP TE 50 rt8 - rt1(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 50 - rt8 16010
+ 10.0.255.2/32 40 - rt8 16020
+ 10.0.255.3/32 50 - rt8 16030
+ 10.0.255.4/32 40 - rt8 16040
+ 10.0.255.5/32 30 - rt8 16050
+ 10.0.255.6/32 40 - rt8 16060
+ 10.0.255.7/32 30 - rt8 16070
+ 10.0.255.8/32 20 - rt8 implicit-null
+ 10.0.255.9/32 30 - rt8 16090
+ - rt12 16090
+ 10.0.255.10/32 20 - rt10 implicit-null
+ 10.0.255.11/32 0 - - -
+ 10.0.255.12/32 20 - rt12 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.1/32 60 - rt10 16010
+ 10.0.255.2/32 60 - rt12 50900/16020
+ 10.0.255.3/32 70 - rt12 50900/16030
+ 10.0.255.4/32 50 - rt10 16040
+ 10.0.255.5/32 50 - rt12 50900/16050
+ 10.0.255.6/32 60 - rt12 50900/16060
+ 10.0.255.7/32 40 - rt10 16070
+ 10.0.255.8/32 40 - rt12 50900/16080
+
+test# test isis topology 7 root rt6 remote-lfa system-id rt5 ipv4-only
+P-space (self):
+ rt3
+
+P-space (rt3):
+ rt2
+ rt3
+
+P-space (rt9):
+ rt1
+ rt2
+ rt4
+ rt5
+ rt7
+ rt8
+ rt9
+ rt10
+ rt11
+ rt12
+
+Q-space:
+ rt1
+ rt2
+ rt4
+ rt5
+ rt7
+ rt8
+ rt9
+ rt10
+ rt11
+ rt12
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt6
+10.0.255.6/32 IP internal 0 rt6(4)
+rt3 TE-IS 10 rt3 - rt6(4)
+rt2 TE-IS 20 rt3 - rt3(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt9 TE-IS 30 rt9 - rt6(4)
+rt5 TE-IS 30 rt3 - rt2(4)
+10.0.255.2/32 IP TE 30 rt3 - rt2(4)
+rt8 TE-IS 40 rt9 - rt9(4)
+ rt3 - rt5(4)
+rt12 TE-IS 40 rt9 - rt9(4)
+rt4 TE-IS 40 rt3 - rt5(4)
+10.0.255.9/32 IP TE 40 rt9 - rt9(4)
+10.0.255.5/32 IP TE 40 rt3 - rt5(4)
+rt7 TE-IS 50 rt9 - rt8(4)
+ rt3 - rt4(4)
+rt11 TE-IS 50 rt9 - rt8(4)
+ rt3 - rt12(4)
+rt1 TE-IS 50 rt3 - rt4(4)
+10.0.255.8/32 IP TE 50 rt9 - rt8(4)
+ rt3 -
+10.0.255.12/32 IP TE 50 rt9 - rt12(4)
+10.0.255.4/32 IP TE 50 rt3 - rt4(4)
+rt10 TE-IS 60 rt9 - rt11(4)
+ rt3 -
+10.0.255.7/32 IP TE 60 rt9 - rt7(4)
+ rt3 -
+10.0.255.11/32 IP TE 60 rt9 - rt11(4)
+ rt3 -
+10.0.255.1/32 IP TE 60 rt3 - rt1(4)
+10.0.255.10/32 IP TE 70 rt9 - rt10(4)
+ rt3 -
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt6
+10.0.255.6/32 IP internal 0 rt6(4)
+rt3 TE-IS 10 rt3 - rt6(4)
+rt5 TE-IS 10 rt5 - rt6(4)
+rt2 TE-IS 20 rt3 - rt3(4)
+ rt5 - rt5(4)
+rt4 TE-IS 20 rt5 - rt5(4)
+rt8 TE-IS 20 rt5 - rt5(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+rt9 TE-IS 30 rt9 - rt6(4)
+ rt5 - rt8(4)
+rt1 TE-IS 30 rt5 - rt4(4)
+rt7 TE-IS 30 rt5 - rt4(4)
+ rt8(4)
+rt11 TE-IS 30 rt5 - rt8(4)
+10.0.255.2/32 IP TE 30 rt3 - rt2(4)
+ rt5 -
+10.0.255.4/32 IP TE 30 rt5 - rt4(4)
+10.0.255.8/32 IP TE 30 rt5 - rt8(4)
+rt12 TE-IS 40 rt9 - rt9(4)
+ rt5 - rt11(4)
+rt10 TE-IS 40 rt5 - rt11(4)
+10.0.255.9/32 IP TE 40 rt9 - rt9(4)
+ rt5 -
+10.0.255.1/32 IP TE 40 rt5 - rt1(4)
+10.0.255.7/32 IP TE 40 rt5 - rt7(4)
+10.0.255.11/32 IP TE 40 rt5 - rt11(4)
+10.0.255.12/32 IP TE 50 rt9 - rt12(4)
+ rt5 -
+10.0.255.10/32 IP TE 50 rt5 - rt10(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 40 - rt5 16010
+ 10.0.255.2/32 30 - rt3 16020
+ - rt5 16020
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt5 16040
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 0 - - -
+ 10.0.255.7/32 40 - rt5 16070
+ 10.0.255.8/32 30 - rt5 16080
+ 10.0.255.9/32 40 - rt9 implicit-null
+ - rt5 implicit-null
+ 10.0.255.10/32 50 - rt5 16100
+ 10.0.255.11/32 40 - rt5 16110
+ 10.0.255.12/32 50 - rt9 16120
+ - rt5 16120
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------
+ 10.0.255.1/32 80 - rt9 16010
+ 10.0.255.4/32 70 - rt9 16040
+ 10.0.255.5/32 60 - rt9 16050
+ 10.0.255.7/32 60 - rt9 16070
+ 10.0.255.8/32 50 - rt9 16080
+ 10.0.255.10/32 70 - rt9 16100
+ 10.0.255.11/32 60 - rt9 16110
+
+test# test isis topology 8 root rt2 remote-lfa system-id rt5 ipv4-only
+P-space (self):
+ rt1
+ rt3
+ rt4
+ rt7
+ rt10
+
+P-space (rt1):
+ rt1
+ rt4
+ rt7
+ rt10
+
+P-space (rt3):
+ rt3
+ rt6
+
+Q-space:
+ rt5
+ rt6
+ rt8
+ rt9
+ rt11
+ rt12
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt1 TE-IS 10 rt1 - rt2(4)
+rt3 TE-IS 10 rt3 - rt2(4)
+rt4 TE-IS 20 rt1 - rt1(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt1 - rt4(4)
+rt5 TE-IS 30 rt3 - rt6(4)
+10.0.255.4/32 IP TE 30 rt1 - rt4(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+rt10 TE-IS 40 rt1 - rt7(4)
+rt8 TE-IS 40 rt3 - rt5(4)
+10.0.255.7/32 IP TE 40 rt1 - rt7(4)
+10.0.255.5/32 IP TE 40 rt3 - rt5(4)
+rt9 TE-IS 50 rt3 - rt8(4)
+rt11 TE-IS 50 rt3 - rt8(4)
+10.0.255.10/32 IP TE 50 rt1 - rt10(4)
+10.0.255.8/32 IP TE 50 rt3 - rt8(4)
+rt12 TE-IS 60 rt3 - rt9(4)
+ rt11(4)
+10.0.255.9/32 IP TE 60 rt3 - rt9(4)
+10.0.255.11/32 IP TE 60 rt3 - rt11(4)
+10.0.255.12/32 IP TE 70 rt3 - rt12(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt1 TE-IS 10 rt1 - rt2(4)
+rt3 TE-IS 10 rt3 - rt2(4)
+rt5 TE-IS 10 rt5 - rt2(4)
+rt4 TE-IS 20 rt1 - rt1(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+ rt5 - rt5(4)
+rt8 TE-IS 20 rt5 - rt5(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+rt7 TE-IS 30 rt1 - rt4(4)
+rt9 TE-IS 30 rt5 - rt8(4)
+rt11 TE-IS 30 rt5 - rt8(4)
+10.0.255.4/32 IP TE 30 rt1 - rt4(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+ rt5 -
+10.0.255.8/32 IP TE 30 rt5 - rt8(4)
+rt10 TE-IS 40 rt1 - rt7(4)
+rt12 TE-IS 40 rt5 - rt9(4)
+ rt11(4)
+10.0.255.7/32 IP TE 40 rt1 - rt7(4)
+10.0.255.9/32 IP TE 40 rt5 - rt9(4)
+10.0.255.11/32 IP TE 40 rt5 - rt11(4)
+10.0.255.10/32 IP TE 50 rt1 - rt10(4)
+10.0.255.12/32 IP TE 50 rt5 - rt12(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 20 - rt1 implicit-null
+ 10.0.255.2/32 0 - - -
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt1 16040
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 30 - rt3 16060
+ - rt5 16060
+ 10.0.255.7/32 40 - rt1 16070
+ 10.0.255.8/32 30 - rt5 16080
+ 10.0.255.9/32 40 - rt5 16090
+ 10.0.255.10/32 50 - rt1 16100
+ 10.0.255.11/32 40 - rt5 16110
+ 10.0.255.12/32 50 - rt5 16120
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ---------------------------------------------------------
+ 10.0.255.5/32 40 - rt3 50600/16050
+ 10.0.255.8/32 50 - rt3 50600/16080
+ 10.0.255.9/32 60 - rt3 50600/16090
+ 10.0.255.11/32 60 - rt3 50600/16110
+ 10.0.255.12/32 70 - rt3 50600/16120
+
+test# test isis topology 11 root rt2 remote-lfa system-id rt4
+P-space (self):
+
+P-space (rt1):
+ rt1
+ rt3
+ rt5
+
+P-space (rt3):
+ rt1
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt1
+ rt3
+ rt4
+ rt5
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt1 TE-IS 50 rt1 - rt2(4)
+rt3 TE-IS 50 rt3 - rt2(4)
+rt2
+rt5 TE-IS 60 rt3 - rt3(4)
+10.0.255.1/32 IP TE 60 rt1 - rt1(4)
+10.0.255.3/32 IP TE 60 rt3 - rt3(4)
+rt4 TE-IS 70 rt3 - rt5(4)
+rt6 TE-IS 70 rt3 - rt5(4)
+10.0.255.5/32 IP TE 70 rt3 - rt5(4)
+10.0.255.4/32 IP TE 80 rt3 - rt4(4)
+10.0.255.6/32 IP TE 80 rt3 - rt6(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt4 TE-IS 10 rt4 - rt2(4)
+rt5 TE-IS 20 rt4 - rt4(4)
+rt6 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+rt3 TE-IS 30 rt4 - rt5(4)
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)
+10.0.255.6/32 IP TE 30 rt4 - rt6(4)
+rt2
+rt1 TE-IS 40 rt4 - rt2(2)
+10.0.255.3/32 IP TE 40 rt4 - rt3(4)
+10.0.255.1/32 IP TE 50 rt4 - rt1(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 50 - rt4 16010
+ 10.0.255.2/32 0 - - -
+ 10.0.255.3/32 40 - rt4 16030
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 30 - rt4 16050
+ 10.0.255.6/32 30 - rt4 16060
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 60 - rt1 implicit-null
+ - rt3 16010
+ 10.0.255.3/32 60 - rt1 16030
+ - rt3 implicit-null
+ 10.0.255.4/32 80 - rt3 50500/16040
+ 10.0.255.5/32 70 - rt1 16050
+ - rt3 16050
+ 10.0.255.6/32 80 - rt3 16060
+
+P-space (self):
+
+P-space (rt1):
+ rt1
+ rt3
+ rt5
+
+P-space (rt3):
+ rt1
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt1
+ rt3
+ rt4
+ rt5
+ rt6
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+2001:db8::2/128 IP6 internal 0 rt2(4)
+rt1 TE-IS 50 rt1 - rt2(4)
+rt3 TE-IS 50 rt3 - rt2(4)
+rt2
+rt5 TE-IS 60 rt3 - rt3(4)
+2001:db8::1/128 IP6 internal 60 rt1 - rt1(4)
+2001:db8::3/128 IP6 internal 60 rt3 - rt3(4)
+rt4 TE-IS 70 rt3 - rt5(4)
+rt6 TE-IS 70 rt3 - rt5(4)
+2001:db8::5/128 IP6 internal 70 rt3 - rt5(4)
+2001:db8::4/128 IP6 internal 80 rt3 - rt4(4)
+2001:db8::6/128 IP6 internal 80 rt3 - rt6(4)
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+2001:db8::2/128 IP6 internal 0 rt2(4)
+rt4 TE-IS 10 rt4 - rt2(4)
+rt5 TE-IS 20 rt4 - rt4(4)
+rt6 TE-IS 20 rt4 - rt4(4)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+rt3 TE-IS 30 rt4 - rt5(4)
+2001:db8::5/128 IP6 internal 30 rt4 - rt5(4)
+2001:db8::6/128 IP6 internal 30 rt4 - rt6(4)
+rt2
+rt1 TE-IS 40 rt4 - rt2(2)
+2001:db8::3/128 IP6 internal 40 rt4 - rt3(4)
+2001:db8::1/128 IP6 internal 50 rt4 - rt1(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 50 - rt4 16011
+ 2001:db8::2/128 0 - - -
+ 2001:db8::3/128 40 - rt4 16031
+ 2001:db8::4/128 20 - rt4 implicit-null
+ 2001:db8::5/128 30 - rt4 16051
+ 2001:db8::6/128 30 - rt4 16061
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 60 - rt1 implicit-null
+ - rt3 16011
+ 2001:db8::3/128 60 - rt1 16031
+ - rt3 implicit-null
+ 2001:db8::4/128 80 - rt3 50500/16041
+ 2001:db8::5/128 70 - rt1 16051
+ - rt3 16051
+ 2001:db8::6/128 80 - rt3 16061
+
+test# test isis topology 13 root rt1 remote-lfa system-id rt3 ipv4-only
+P-space (self):
+ rt2
+
+P-space (rt2):
+ rt2
+ rt4
+
+Q-space:
+ rt3
+ rt4
+ rt5
+ rt6
+ rt7
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+rt3 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+rt5 TE-IS 40 rt2 - rt3(4)
+rt6 TE-IS 40 rt2 - rt3(4)
+10.0.255.3/32 IP TE 40 rt2 - rt3(4)
+rt7 TE-IS 50 rt2 - rt5(4)
+ rt6(4)
+10.0.255.5/32 IP TE 50 rt2 - rt5(4)
+10.0.255.6/32 IP TE 50 rt2 - rt6(4)
+10.0.255.7/32 IP TE 60 rt2 - rt7(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+ rt3 - rt3(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+ rt6(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+ rt3 -
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ - rt3 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 30 - rt3 16060
+ 10.0.255.7/32 40 - rt3 16070
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.3/32 40 - rt2 50400/16030
+ 10.0.255.5/32 50 - rt2 50400/16050
+ 10.0.255.6/32 50 - rt2 50400/16060
+ 10.0.255.7/32 60 - rt2 50400/16070
+
+test#
+test# test isis topology 1 root rt1 ti-lfa system-id rt2
+P-space (self):
+ rt3
+ rt5
+
+P-space (rt3):
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt2
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt3 - rt5(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt4 TE-IS 40 rt3 - rt6(4)
+10.0.255.6/32 IP TE 40 rt3 - rt6(4)
+rt2 TE-IS 50 rt3 - rt4(4)
+10.0.255.4/32 IP TE 50 rt3 - rt4(4)
+10.0.255.2/32 IP TE 60 rt3 - rt2(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.2/32 60 - rt3 16060/16020
+ 10.0.255.4/32 50 - rt3 16060/16040
+
+P-space (self):
+ rt3
+ rt5
+
+P-space (rt3):
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt2
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt3 - rt5(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+rt4 TE-IS 40 rt3 - rt6(4)
+2001:db8::6/128 IP6 internal 40 rt3 - rt6(4)
+rt2 TE-IS 50 rt3 - rt4(4)
+2001:db8::4/128 IP6 internal 50 rt3 - rt4(4)
+2001:db8::2/128 IP6 internal 60 rt3 - rt2(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 2001:db8::2/128 60 - rt3 16061/16021
+ 2001:db8::4/128 50 - rt3 16061/16041
+
+test# test isis topology 2 root rt1 ti-lfa system-id rt3
+P-space (self):
+ rt2
+ rt4
+ rt5
+ rt6
+
+P-space (rt2):
+ rt2
+
+P-space (rt4):
+ rt2
+ rt4
+ rt5
+ rt6
+
+P-space (rt5):
+ rt2
+ rt4
+ rt5
+ rt6
+
+Q-space:
+ rt3
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt5 TE-IS 10 rt5 - rt1(4)
+rt2 TE-IS 15 rt2 - rt1(4)
+rt1
+rt6 TE-IS 20 rt4 - rt4(4)
+ rt5 - rt5(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+10.0.255.2/32 IP TE 25 rt2 - rt2(4)
+10.0.255.6/32 IP TE 30 rt4 - rt6(4)
+ rt5 -
+rt3 TE-IS 50 rt5 - rt5(4)
+10.0.255.3/32 IP TE 60 rt5 - rt3(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.3/32 60 - rt5 16050/18
+
+P-space (self):
+ rt2
+ rt4
+ rt5
+ rt6
+
+P-space (rt2):
+ rt2
+
+P-space (rt4):
+ rt2
+ rt4
+ rt5
+ rt6
+
+P-space (rt5):
+ rt2
+ rt4
+ rt5
+ rt6
+
+Q-space:
+ rt3
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt5 TE-IS 10 rt5 - rt1(4)
+rt2 TE-IS 15 rt2 - rt1(4)
+rt1
+rt6 TE-IS 20 rt4 - rt4(4)
+ rt5 - rt5(4)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+2001:db8::5/128 IP6 internal 20 rt5 - rt5(4)
+2001:db8::2/128 IP6 internal 25 rt2 - rt2(4)
+2001:db8::6/128 IP6 internal 30 rt4 - rt6(4)
+ rt5 -
+rt3 TE-IS 50 rt5 - rt5(4)
+2001:db8::3/128 IP6 internal 60 rt5 - rt3(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::3/128 60 - rt5 16051/19
+
+test# test isis topology 2 root rt1 ti-lfa system-id rt1 pseudonode-id 1
+P-space (self):
+ rt2
+ rt3
+
+P-space (rt2):
+ rt2
+ rt3
+
+P-space (rt3):
+ rt2
+ rt3
+
+Q-space:
+ rt4
+ rt5
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 15 rt2 - rt1(4)
+10.0.255.2/32 IP TE 25 rt2 - rt2(4)
+rt3 TE-IS 30 rt3 - rt1(4)
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)
+rt4 TE-IS 55 rt2 - rt2(4)
+rt1
+rt6 TE-IS 65 rt2 - rt4(4)
+rt5 TE-IS 65 rt2 - rt1(2)
+10.0.255.4/32 IP TE 65 rt2 - rt4(4)
+10.0.255.6/32 IP TE 75 rt2 - rt6(4)
+10.0.255.5/32 IP TE 75 rt2 - rt5(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.4/32 65 - rt2 16020/18
+ 10.0.255.5/32 75 - rt2 16020/18/16050
+ 10.0.255.6/32 75 - rt2 16020/18/16060
+
+P-space (self):
+ rt2
+ rt3
+
+P-space (rt2):
+ rt2
+ rt3
+
+P-space (rt3):
+ rt2
+ rt3
+
+Q-space:
+ rt4
+ rt5
+ rt6
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 15 rt2 - rt1(4)
+2001:db8::2/128 IP6 internal 25 rt2 - rt2(4)
+rt3 TE-IS 30 rt3 - rt1(4)
+2001:db8::3/128 IP6 internal 40 rt3 - rt3(4)
+rt4 TE-IS 55 rt2 - rt2(4)
+rt1
+rt6 TE-IS 65 rt2 - rt4(4)
+rt5 TE-IS 65 rt2 - rt1(2)
+2001:db8::4/128 IP6 internal 65 rt2 - rt4(4)
+2001:db8::6/128 IP6 internal 75 rt2 - rt6(4)
+2001:db8::5/128 IP6 internal 75 rt2 - rt5(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------------
+ 2001:db8::4/128 65 - rt2 16021/19
+ 2001:db8::5/128 75 - rt2 16021/19/16051
+ 2001:db8::6/128 75 - rt2 16021/19/16061
+
+test# test isis topology 2 root rt5 ti-lfa system-id rt1 pseudonode-id 1
+P-space (self):
+ rt6
+
+P-space (rt3):
+ rt1
+ rt2
+ rt3
+ rt4
+
+P-space (rt6):
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt4 TE-IS 20 rt6 - rt6(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt1 pseudo_TE-IS 30 rt6 - rt4(4)
+rt1 TE-IS 30 rt6 - rt1(2)
+10.0.255.4/32 IP TE 30 rt6 - rt4(4)
+rt3 TE-IS 40 rt3 - rt5(4)
+10.0.255.1/32 IP TE 40 rt6 - rt1(4)
+rt2 TE-IS 45 rt6 - rt1(4)
+10.0.255.3/32 IP TE 50 rt3 - rt3(4)
+10.0.255.2/32 IP TE 55 rt6 - rt2(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.1/32 40 - rt6 16040/16010
+ 10.0.255.2/32 55 - rt6 16040/16020
+ 10.0.255.4/32 30 - rt6 16040
+
+P-space (self):
+ rt6
+
+P-space (rt3):
+ rt1
+ rt2
+ rt3
+ rt4
+
+P-space (rt6):
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+2001:db8::5/128 IP6 internal 0 rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt4 TE-IS 20 rt6 - rt6(4)
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)
+rt1 pseudo_TE-IS 30 rt6 - rt4(4)
+rt1 TE-IS 30 rt6 - rt1(2)
+2001:db8::4/128 IP6 internal 30 rt6 - rt4(4)
+rt3 TE-IS 40 rt3 - rt5(4)
+2001:db8::1/128 IP6 internal 40 rt6 - rt1(4)
+rt2 TE-IS 45 rt6 - rt1(4)
+2001:db8::3/128 IP6 internal 50 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 55 rt6 - rt2(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 2001:db8::1/128 40 - rt6 16041/16011
+ 2001:db8::2/128 55 - rt6 16041/16021
+ 2001:db8::4/128 30 - rt6 16041
+
+test# test isis topology 3 root rt5 ti-lfa system-id rt4 ipv4-only
+P-space (self):
+ rt6
+
+P-space (rt3):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+P-space (rt6):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt4 TE-IS 20 rt6 - rt6(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt3 - rt5(4)
+rt2 TE-IS 30 rt6 - rt4(4)
+10.0.255.4/32 IP TE 30 rt6 - rt4(4)
+rt1 TE-IS 40 rt3 - rt3(4)
+ rt6 - rt2(4)
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)
+10.0.255.2/32 IP TE 40 rt6 - rt2(4)
+10.0.255.1/32 IP TE 50 rt3 - rt1(4)
+ rt6 -
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.1/32 50 - rt3 16010
+ - rt6 16010
+ 10.0.255.2/32 40 - rt6 16020
+ 10.0.255.4/32 30 - rt6 16040
+
+test# test isis topology 3 root rt5 ti-lfa system-id rt3 ipv4-only
+P-space (self):
+ rt1
+ rt2
+ rt4
+ rt6
+
+P-space (rt4):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+P-space (rt6):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt2 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt1 TE-IS 30 rt4 - rt2(4)
+rt3 TE-IS 30 rt4 - rt2(4)
+10.0.255.2/32 IP TE 30 rt4 - rt2(4)
+10.0.255.1/32 IP TE 40 rt4 - rt1(4)
+10.0.255.3/32 IP TE 40 rt4 - rt3(4)
+
+IS-IS L1 IPv4 routing table:
+
+test# test isis topology 4 root rt1 ti-lfa system-id rt2 ipv4-only
+P-space (self):
+ rt3
+ rt5
+ rt7
+
+P-space (rt3):
+ rt3
+ rt5
+ rt7
+
+Q-space:
+ rt2
+ rt4
+ rt6
+ rt8
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+rt6 TE-IS 70 rt3 - rt5(4)
+rt4 TE-IS 80 rt3 - rt6(4)
+rt8 TE-IS 80 rt3 - rt6(4)
+10.0.255.6/32 IP TE 80 rt3 - rt6(4)
+rt2 TE-IS 90 rt3 - rt4(4)
+10.0.255.4/32 IP TE 90 rt3 - rt4(4)
+10.0.255.8/32 IP TE 90 rt3 - rt8(4)
+10.0.255.2/32 IP TE 100 rt3 - rt2(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.2/32 100 - rt3 16050/17/16020
+ 10.0.255.4/32 90 - rt3 16050/17/16040
+ 10.0.255.6/32 80 - rt3 16050/17
+ 10.0.255.8/32 90 - rt3 16050/17/16080
+
+test# test isis topology 4 root rt4 ti-lfa system-id rt6 ipv4-only
+P-space (self):
+ rt1
+ rt2
+ rt3
+ rt5
+ rt7
+
+P-space (rt2):
+ rt1
+ rt2
+ rt3
+ rt5
+ rt7
+
+Q-space:
+ rt6
+ rt8
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+10.0.255.4/32 IP internal 0 rt4(4)
+rt2 TE-IS 10 rt2 - rt4(4)
+rt1 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+rt3 TE-IS 30 rt2 - rt1(4)
+10.0.255.1/32 IP TE 30 rt2 - rt1(4)
+rt5 TE-IS 40 rt2 - rt3(4)
+10.0.255.3/32 IP TE 40 rt2 - rt3(4)
+rt7 TE-IS 50 rt2 - rt5(4)
+10.0.255.5/32 IP TE 50 rt2 - rt5(4)
+10.0.255.7/32 IP TE 60 rt2 - rt7(4)
+rt6 TE-IS 90 rt2 - rt5(4)
+rt8 TE-IS 100 rt2 - rt6(4)
+10.0.255.6/32 IP TE 100 rt2 - rt6(4)
+10.0.255.8/32 IP TE 110 rt2 - rt8(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.6/32 100 - rt2 16050/17
+ 10.0.255.8/32 110 - rt2 16050/17/16080
+
+test# test isis topology 5 root rt1 ti-lfa system-id rt2 ipv4-only
+P-space (self):
+ rt3
+ rt5
+ rt7
+
+P-space (rt3):
+ rt3
+ rt5
+ rt7
+ rt8
+
+Q-space:
+ rt2
+ rt4
+ rt6
+ rt8
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt3 - rt7(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+rt6 TE-IS 50 rt3 - rt8(4)
+10.0.255.8/32 IP TE 50 rt3 - rt8(4)
+rt4 TE-IS 60 rt3 - rt6(4)
+10.0.255.6/32 IP TE 60 rt3 - rt6(4)
+rt2 TE-IS 70 rt3 - rt4(4)
+10.0.255.4/32 IP TE 70 rt3 - rt4(4)
+10.0.255.2/32 IP TE 80 rt3 - rt2(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.2/32 80 - rt3 16080/16020
+ 10.0.255.4/32 70 - rt3 16080/16040
+ 10.0.255.6/32 60 - rt3 16080/16060
+
+test# test isis topology 6 root rt4 ti-lfa system-id rt3 ipv4-only
+P-space (self):
+ rt2
+ rt5
+ rt6
+ rt7
+ rt8
+
+P-space (rt2):
+ rt1
+ rt2
+
+P-space (rt6):
+ rt5
+ rt6
+ rt7
+ rt8
+
+Q-space:
+ rt1
+ rt3
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+10.0.255.4/32 IP internal 0 rt4(4)
+rt2 TE-IS 10 rt2 - rt4(4)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt6 - rt6(4)
+rt8 TE-IS 20 rt6 - rt6(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt2 - rt1(4)
+rt7 TE-IS 30 rt6 - rt5(4)
+ rt8(4)
+10.0.255.1/32 IP TE 30 rt2 - rt1(4)
+10.0.255.5/32 IP TE 30 rt6 - rt5(4)
+10.0.255.8/32 IP TE 30 rt6 - rt8(4)
+10.0.255.3/32 IP TE 40 rt2 - rt3(4)
+10.0.255.7/32 IP TE 40 rt6 - rt7(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.3/32 40 - rt2 16010/16030
+
+test# test isis topology 7 root rt11 ti-lfa system-id rt8 ipv4-only
+P-space (self):
+ rt10
+ rt12
+
+P-space (rt10):
+ rt1
+ rt4
+ rt7
+ rt10
+
+P-space (rt12):
+ rt9
+ rt12
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt5
+ rt6
+ rt7
+ rt8
+ rt9
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt11
+10.0.255.11/32 IP internal 0 rt11(4)
+rt10 TE-IS 10 rt10 - rt11(4)
+rt12 TE-IS 10 rt12 - rt11(4)
+rt9 TE-IS 20 rt12 - rt12(4)
+10.0.255.10/32 IP TE 20 rt10 - rt10(4)
+10.0.255.12/32 IP TE 20 rt12 - rt12(4)
+rt7 TE-IS 30 rt10 - rt10(4)
+rt8 TE-IS 30 rt12 - rt9(4)
+10.0.255.9/32 IP TE 30 rt12 - rt9(4)
+rt4 TE-IS 40 rt10 - rt7(4)
+rt5 TE-IS 40 rt12 - rt8(4)
+10.0.255.7/32 IP TE 40 rt10 - rt7(4)
+10.0.255.8/32 IP TE 40 rt12 - rt8(4)
+rt6 TE-IS 50 rt12 - rt9(4)
+ rt5(4)
+rt1 TE-IS 50 rt10 - rt4(4)
+rt2 TE-IS 50 rt12 - rt5(4)
+10.0.255.4/32 IP TE 50 rt10 - rt4(4)
+10.0.255.5/32 IP TE 50 rt12 - rt5(4)
+rt3 TE-IS 60 rt12 - rt6(4)
+ rt2(4)
+10.0.255.6/32 IP TE 60 rt12 - rt6(4)
+10.0.255.1/32 IP TE 60 rt10 - rt1(4)
+10.0.255.2/32 IP TE 60 rt12 - rt2(4)
+10.0.255.3/32 IP TE 70 rt12 - rt3(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.1/32 60 - rt10 16010
+ 10.0.255.2/32 60 - rt12 16090/16020
+ 10.0.255.3/32 70 - rt12 16090/16030
+ 10.0.255.4/32 50 - rt10 16040
+ 10.0.255.5/32 50 - rt12 16090/16050
+ 10.0.255.6/32 60 - rt12 16090/16060
+ 10.0.255.7/32 40 - rt10 16070
+ 10.0.255.8/32 40 - rt12 16090/16080
+
+test# test isis topology 7 root rt6 ti-lfa system-id rt5 ipv4-only
+P-space (self):
+ rt3
+
+P-space (rt3):
+ rt2
+ rt3
+
+P-space (rt9):
+ rt1
+ rt2
+ rt4
+ rt5
+ rt7
+ rt8
+ rt9
+ rt10
+ rt11
+ rt12
+
+Q-space:
+ rt1
+ rt2
+ rt4
+ rt5
+ rt7
+ rt8
+ rt9
+ rt10
+ rt11
+ rt12
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt6
+10.0.255.6/32 IP internal 0 rt6(4)
+rt3 TE-IS 10 rt3 - rt6(4)
+rt2 TE-IS 20 rt3 - rt3(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt9 TE-IS 30 rt9 - rt6(4)
+rt5 TE-IS 30 rt3 - rt2(4)
+10.0.255.2/32 IP TE 30 rt3 - rt2(4)
+rt8 TE-IS 40 rt9 - rt9(4)
+ rt3 - rt5(4)
+rt12 TE-IS 40 rt9 - rt9(4)
+rt4 TE-IS 40 rt3 - rt5(4)
+10.0.255.9/32 IP TE 40 rt9 - rt9(4)
+10.0.255.5/32 IP TE 40 rt3 - rt5(4)
+rt7 TE-IS 50 rt9 - rt8(4)
+ rt3 - rt4(4)
+rt11 TE-IS 50 rt9 - rt8(4)
+ rt3 - rt12(4)
+rt1 TE-IS 50 rt3 - rt4(4)
+10.0.255.8/32 IP TE 50 rt9 - rt8(4)
+ rt3 -
+10.0.255.12/32 IP TE 50 rt9 - rt12(4)
+10.0.255.4/32 IP TE 50 rt3 - rt4(4)
+rt10 TE-IS 60 rt9 - rt11(4)
+ rt3 -
+10.0.255.7/32 IP TE 60 rt9 - rt7(4)
+ rt3 -
+10.0.255.11/32 IP TE 60 rt9 - rt11(4)
+ rt3 -
+10.0.255.1/32 IP TE 60 rt3 - rt1(4)
+10.0.255.10/32 IP TE 70 rt9 - rt10(4)
+ rt3 -
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ---------------------------------------------------------
+ 10.0.255.1/32 60 - rt3 16020/16010
+ 10.0.255.4/32 50 - rt3 16020/16040
+ 10.0.255.5/32 40 - rt3 16020/16050
+ 10.0.255.7/32 60 - rt9 16070
+ - rt3 16070
+ 10.0.255.8/32 50 - rt9 16080
+ - rt3 16080
+ 10.0.255.10/32 70 - rt9 16100
+ - rt3 16100
+ 10.0.255.11/32 60 - rt9 16110
+ - rt3 16110
+
+test# test isis topology 8 root rt2 ti-lfa system-id rt1 ipv4-only
+P-space (self):
+ rt3
+ rt5
+ rt6
+ rt8
+ rt9
+ rt11
+ rt12
+
+P-space (rt3):
+ rt3
+ rt6
+
+P-space (rt5):
+ rt5
+ rt6
+ rt8
+ rt9
+ rt11
+ rt12
+
+Q-space:
+ rt1
+ rt4
+ rt7
+ rt10
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt3 TE-IS 10 rt3 - rt2(4)
+rt5 TE-IS 10 rt5 - rt2(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+ rt5 - rt5(4)
+rt8 TE-IS 20 rt5 - rt5(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+rt9 TE-IS 30 rt5 - rt8(4)
+rt11 TE-IS 30 rt5 - rt8(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+ rt5 -
+10.0.255.8/32 IP TE 30 rt5 - rt8(4)
+rt12 TE-IS 40 rt5 - rt9(4)
+ rt11(4)
+10.0.255.9/32 IP TE 40 rt5 - rt9(4)
+10.0.255.11/32 IP TE 40 rt5 - rt11(4)
+10.0.255.12/32 IP TE 50 rt5 - rt12(4)
+rt10 TE-IS 60 rt5 - rt11(4)
+rt7 TE-IS 70 rt5 - rt10(4)
+10.0.255.10/32 IP TE 70 rt5 - rt10(4)
+rt4 TE-IS 80 rt5 - rt7(4)
+10.0.255.7/32 IP TE 80 rt5 - rt7(4)
+rt1 TE-IS 90 rt5 - rt4(4)
+10.0.255.4/32 IP TE 90 rt5 - rt4(4)
+10.0.255.1/32 IP TE 100 rt5 - rt1(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 10.0.255.1/32 100 - rt5 16110/17/16010
+ 10.0.255.4/32 90 - rt5 16110/17/16040
+ 10.0.255.7/32 80 - rt5 16110/17/16070
+ 10.0.255.10/32 70 - rt5 16110/17
+
+test# test isis topology 8 root rt2 ti-lfa system-id rt5 ipv4-only
+P-space (self):
+ rt1
+ rt3
+ rt4
+ rt7
+ rt10
+
+P-space (rt1):
+ rt1
+ rt4
+ rt7
+ rt10
+
+P-space (rt3):
+ rt3
+ rt6
+
+Q-space:
+ rt5
+ rt6
+ rt8
+ rt9
+ rt11
+ rt12
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt1 TE-IS 10 rt1 - rt2(4)
+rt3 TE-IS 10 rt3 - rt2(4)
+rt4 TE-IS 20 rt1 - rt1(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt1 - rt4(4)
+rt5 TE-IS 30 rt3 - rt6(4)
+10.0.255.4/32 IP TE 30 rt1 - rt4(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+rt10 TE-IS 40 rt1 - rt7(4)
+rt8 TE-IS 40 rt3 - rt5(4)
+10.0.255.7/32 IP TE 40 rt1 - rt7(4)
+10.0.255.5/32 IP TE 40 rt3 - rt5(4)
+rt9 TE-IS 50 rt3 - rt8(4)
+rt11 TE-IS 50 rt3 - rt8(4)
+10.0.255.10/32 IP TE 50 rt1 - rt10(4)
+10.0.255.8/32 IP TE 50 rt3 - rt8(4)
+rt12 TE-IS 60 rt3 - rt9(4)
+ rt11(4)
+10.0.255.9/32 IP TE 60 rt3 - rt9(4)
+10.0.255.11/32 IP TE 60 rt3 - rt11(4)
+10.0.255.12/32 IP TE 70 rt3 - rt12(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ---------------------------------------------------------
+ 10.0.255.5/32 40 - rt3 16060/16050
+ 10.0.255.8/32 50 - rt3 16060/16080
+ 10.0.255.9/32 60 - rt3 16060/16090
+ 10.0.255.11/32 60 - rt3 16060/16110
+ 10.0.255.12/32 70 - rt3 16060/16120
+
+test# test isis topology 9 root rt1 ti-lfa system-id rt3
+P-space (self):
+ rt2
+ rt4
+ rt5
+ rt6
+ rt7
+ rt8
+ rt9
+
+P-space (rt2):
+ rt2
+ rt4
+ rt5
+ rt6
+ rt7
+ rt8
+ rt9
+
+Q-space:
+ rt3
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+rt5 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+rt9 TE-IS 40 rt2 - rt5(4)
+10.0.255.5/32 IP TE 40 rt2 - rt5(4)
+rt6 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+rt7 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+rt8 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+10.0.255.9/32 IP TE 50 rt2 - rt9(4)
+10.0.255.6/32 IP TE 60 rt2 - rt6(4)
+10.0.255.7/32 IP TE 60 rt2 - rt7(4)
+10.0.255.8/32 IP TE 60 rt2 - rt8(4)
+rt3 TE-IS 120 rt2 - rt4(4)
+10.0.255.3/32 IP TE 130 rt2 - rt3(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.3/32 130 - rt2 16040/18
+
+P-space (self):
+ rt2
+ rt4
+ rt5
+ rt6
+ rt7
+ rt8
+ rt9
+
+P-space (rt2):
+ rt2
+ rt4
+ rt5
+ rt6
+ rt7
+ rt8
+ rt9
+
+Q-space:
+ rt3
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+rt5 TE-IS 30 rt2 - rt4(4)
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)
+rt9 TE-IS 40 rt2 - rt5(4)
+2001:db8::5/128 IP6 internal 40 rt2 - rt5(4)
+rt6 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+rt7 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+rt8 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+2001:db8::9/128 IP6 internal 50 rt2 - rt9(4)
+2001:db8::6/128 IP6 internal 60 rt2 - rt6(4)
+2001:db8::7/128 IP6 internal 60 rt2 - rt7(4)
+2001:db8::8/128 IP6 internal 60 rt2 - rt8(4)
+rt3 TE-IS 120 rt2 - rt4(4)
+2001:db8::3/128 IP6 internal 130 rt2 - rt3(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::3/128 130 - rt2 16041/19
+
+test# test isis topology 9 root rt1 ti-lfa system-id rt2
+P-space (self):
+ rt3
+
+P-space (rt3):
+ rt3
+
+Q-space:
+ rt2
+ rt4
+ rt5
+ rt6
+ rt7
+ rt8
+ rt9
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt4 TE-IS 110 rt3 - rt3(4)
+rt2 TE-IS 120 rt3 - rt4(4)
+rt5 TE-IS 120 rt3 - rt4(4)
+10.0.255.4/32 IP TE 120 rt3 - rt4(4)
+rt9 TE-IS 130 rt3 - rt5(4)
+10.0.255.2/32 IP TE 130 rt3 - rt2(4)
+10.0.255.5/32 IP TE 130 rt3 - rt5(4)
+rt6 TE-IS 140 rt3 - rt4(4)
+ rt9(4)
+rt7 TE-IS 140 rt3 - rt4(4)
+ rt9(4)
+rt8 TE-IS 140 rt3 - rt4(4)
+ rt9(4)
+10.0.255.9/32 IP TE 140 rt3 - rt9(4)
+10.0.255.6/32 IP TE 150 rt3 - rt6(4)
+10.0.255.7/32 IP TE 150 rt3 - rt7(4)
+10.0.255.8/32 IP TE 150 rt3 - rt8(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.2/32 130 - rt3 16030/18/16020
+ 10.0.255.4/32 120 - rt3 16030/18
+ 10.0.255.5/32 130 - rt3 16030/18/16050
+ 10.0.255.6/32 150 - rt3 16030/18/16060
+ 10.0.255.7/32 150 - rt3 16030/18/16070
+ 10.0.255.8/32 150 - rt3 16030/18/16080
+ 10.0.255.9/32 140 - rt3 16030/18/16090
+
+P-space (self):
+ rt3
+
+P-space (rt3):
+ rt3
+
+Q-space:
+ rt2
+ rt4
+ rt5
+ rt6
+ rt7
+ rt8
+ rt9
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt4 TE-IS 110 rt3 - rt3(4)
+rt2 TE-IS 120 rt3 - rt4(4)
+rt5 TE-IS 120 rt3 - rt4(4)
+2001:db8::4/128 IP6 internal 120 rt3 - rt4(4)
+rt9 TE-IS 130 rt3 - rt5(4)
+2001:db8::2/128 IP6 internal 130 rt3 - rt2(4)
+2001:db8::5/128 IP6 internal 130 rt3 - rt5(4)
+rt6 TE-IS 140 rt3 - rt4(4)
+ rt9(4)
+rt7 TE-IS 140 rt3 - rt4(4)
+ rt9(4)
+rt8 TE-IS 140 rt3 - rt4(4)
+ rt9(4)
+2001:db8::9/128 IP6 internal 140 rt3 - rt9(4)
+2001:db8::6/128 IP6 internal 150 rt3 - rt6(4)
+2001:db8::7/128 IP6 internal 150 rt3 - rt7(4)
+2001:db8::8/128 IP6 internal 150 rt3 - rt8(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------------
+ 2001:db8::2/128 130 - rt3 16031/19/16021
+ 2001:db8::4/128 120 - rt3 16031/19
+ 2001:db8::5/128 130 - rt3 16031/19/16051
+ 2001:db8::6/128 150 - rt3 16031/19/16061
+ 2001:db8::7/128 150 - rt3 16031/19/16071
+ 2001:db8::8/128 150 - rt3 16031/19/16081
+ 2001:db8::9/128 140 - rt3 16031/19/16091
+
+test# test isis topology 9 root rt9 ti-lfa system-id rt5
+P-space (self):
+ rt6
+ rt7
+ rt8
+
+P-space (rt6):
+ rt6
+
+P-space (rt7):
+ rt7
+
+P-space (rt8):
+ rt8
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt5
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt9
+10.0.255.9/32 IP internal 0 rt9(4)
+rt6 TE-IS 10 rt6 - rt9(4)
+rt7 TE-IS 10 rt7 - rt9(4)
+rt8 TE-IS 10 rt8 - rt9(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+10.0.255.7/32 IP TE 20 rt7 - rt7(4)
+10.0.255.8/32 IP TE 20 rt8 - rt8(4)
+rt4 TE-IS 40 rt6 - rt6(4)
+ rt7 - rt7(4)
+ rt8 - rt8(4)
+rt2 TE-IS 50 rt6 - rt4(4)
+ rt7 -
+ rt8 -
+rt5 TE-IS 50 rt6 - rt4(4)
+ rt7 -
+ rt8 -
+10.0.255.4/32 IP TE 50 rt6 - rt4(4)
+ rt7 -
+ rt8 -
+rt1 TE-IS 60 rt6 - rt2(4)
+ rt7 -
+ rt8 -
+10.0.255.2/32 IP TE 60 rt6 - rt2(4)
+ rt7 -
+ rt8 -
+10.0.255.5/32 IP TE 60 rt6 - rt5(4)
+ rt7 -
+ rt8 -
+rt3 TE-IS 70 rt6 - rt1(4)
+ rt7 -
+ rt8 -
+10.0.255.1/32 IP TE 70 rt6 - rt1(4)
+ rt7 -
+ rt8 -
+10.0.255.3/32 IP TE 80 rt6 - rt3(4)
+ rt7 -
+ rt8 -
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 70 - rt6 16060/16/16010
+ - rt7 16070/16/16010
+ - rt8 16080/16/16010
+ 10.0.255.2/32 60 - rt6 16060/16/16020
+ - rt7 16070/16/16020
+ - rt8 16080/16/16020
+ 10.0.255.3/32 80 - rt6 16060/16/16030
+ - rt7 16070/16/16030
+ - rt8 16080/16/16030
+ 10.0.255.4/32 50 - rt6 16060/16
+ - rt7 16070/16
+ - rt8 16080/16
+ 10.0.255.5/32 60 - rt6 16060/16/16050
+ - rt7 16070/16/16050
+ - rt8 16080/16/16050
+
+P-space (self):
+ rt6
+ rt7
+ rt8
+
+P-space (rt6):
+ rt6
+
+P-space (rt7):
+ rt7
+
+P-space (rt8):
+ rt8
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt5
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt9
+2001:db8::9/128 IP6 internal 0 rt9(4)
+rt6 TE-IS 10 rt6 - rt9(4)
+rt7 TE-IS 10 rt7 - rt9(4)
+rt8 TE-IS 10 rt8 - rt9(4)
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)
+2001:db8::7/128 IP6 internal 20 rt7 - rt7(4)
+2001:db8::8/128 IP6 internal 20 rt8 - rt8(4)
+rt4 TE-IS 40 rt6 - rt6(4)
+ rt7 - rt7(4)
+ rt8 - rt8(4)
+rt2 TE-IS 50 rt6 - rt4(4)
+ rt7 -
+ rt8 -
+rt5 TE-IS 50 rt6 - rt4(4)
+ rt7 -
+ rt8 -
+2001:db8::4/128 IP6 internal 50 rt6 - rt4(4)
+ rt7 -
+ rt8 -
+rt1 TE-IS 60 rt6 - rt2(4)
+ rt7 -
+ rt8 -
+2001:db8::2/128 IP6 internal 60 rt6 - rt2(4)
+ rt7 -
+ rt8 -
+2001:db8::5/128 IP6 internal 60 rt6 - rt5(4)
+ rt7 -
+ rt8 -
+rt3 TE-IS 70 rt6 - rt1(4)
+ rt7 -
+ rt8 -
+2001:db8::1/128 IP6 internal 70 rt6 - rt1(4)
+ rt7 -
+ rt8 -
+2001:db8::3/128 IP6 internal 80 rt6 - rt3(4)
+ rt7 -
+ rt8 -
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------------
+ 2001:db8::1/128 70 - rt6 16061/17/16011
+ - rt7 16071/17/16011
+ - rt8 16081/17/16011
+ 2001:db8::2/128 60 - rt6 16061/17/16021
+ - rt7 16071/17/16021
+ - rt8 16081/17/16021
+ 2001:db8::3/128 80 - rt6 16061/17/16031
+ - rt7 16071/17/16031
+ - rt8 16081/17/16031
+ 2001:db8::4/128 50 - rt6 16061/17
+ - rt7 16071/17
+ - rt8 16081/17
+ 2001:db8::5/128 60 - rt6 16061/17/16051
+ - rt7 16071/17/16051
+ - rt8 16081/17/16051
+
+test# test isis topology 9 root rt9 ti-lfa system-id rt8
+P-space (self):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt5
+ rt6
+ rt7
+
+P-space (rt5):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt5
+
+P-space (rt6):
+ rt6
+
+P-space (rt7):
+ rt7
+
+Q-space:
+ rt8
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt9
+10.0.255.9/32 IP internal 0 rt9(4)
+rt5 TE-IS 10 rt5 - rt9(4)
+rt6 TE-IS 10 rt6 - rt9(4)
+rt7 TE-IS 10 rt7 - rt9(4)
+rt4 TE-IS 20 rt5 - rt5(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+10.0.255.7/32 IP TE 20 rt7 - rt7(4)
+rt2 TE-IS 30 rt5 - rt4(4)
+10.0.255.4/32 IP TE 30 rt5 - rt4(4)
+rt1 TE-IS 40 rt5 - rt2(4)
+10.0.255.2/32 IP TE 40 rt5 - rt2(4)
+rt8 TE-IS 50 rt5 - rt4(4)
+rt3 TE-IS 50 rt5 - rt1(4)
+10.0.255.1/32 IP TE 50 rt5 - rt1(4)
+10.0.255.8/32 IP TE 60 rt5 - rt8(4)
+10.0.255.3/32 IP TE 60 rt5 - rt3(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.8/32 60 - rt5 16040/26
+
+P-space (self):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt5
+ rt6
+ rt7
+
+P-space (rt5):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt5
+
+P-space (rt6):
+ rt6
+
+P-space (rt7):
+ rt7
+
+Q-space:
+ rt8
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt9
+2001:db8::9/128 IP6 internal 0 rt9(4)
+rt5 TE-IS 10 rt5 - rt9(4)
+rt6 TE-IS 10 rt6 - rt9(4)
+rt7 TE-IS 10 rt7 - rt9(4)
+rt4 TE-IS 20 rt5 - rt5(4)
+2001:db8::5/128 IP6 internal 20 rt5 - rt5(4)
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)
+2001:db8::7/128 IP6 internal 20 rt7 - rt7(4)
+rt2 TE-IS 30 rt5 - rt4(4)
+2001:db8::4/128 IP6 internal 30 rt5 - rt4(4)
+rt1 TE-IS 40 rt5 - rt2(4)
+2001:db8::2/128 IP6 internal 40 rt5 - rt2(4)
+rt8 TE-IS 50 rt5 - rt4(4)
+rt3 TE-IS 50 rt5 - rt1(4)
+2001:db8::1/128 IP6 internal 50 rt5 - rt1(4)
+2001:db8::8/128 IP6 internal 60 rt5 - rt8(4)
+2001:db8::3/128 IP6 internal 60 rt5 - rt3(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::8/128 60 - rt5 16041/27
+
+test# test isis topology 10 root rt1 ti-lfa system-id rt2
+P-space (self):
+ rt3
+ rt4
+ rt6
+ rt7
+
+P-space (rt3):
+ rt3
+ rt6
+
+P-space (rt4):
+ rt4
+ rt7
+
+Q-space:
+ rt2
+ rt5
+ rt8
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt3 TE-IS 20 rt3 - rt1(4)
+rt4 TE-IS 20 rt4 - rt1(4)
+rt6 TE-IS 30 rt3 - rt3(4)
+rt7 TE-IS 30 rt4 - rt4(4)
+10.0.255.3/32 IP TE 30 rt3 - rt3(4)
+10.0.255.4/32 IP TE 30 rt4 - rt4(4)
+10.0.255.6/32 IP TE 40 rt3 - rt6(4)
+10.0.255.7/32 IP TE 40 rt4 - rt7(4)
+rt8 TE-IS 80 rt3 - rt6(4)
+ rt4 - rt7(4)
+rt5 TE-IS 90 rt3 - rt8(4)
+ rt4 -
+10.0.255.8/32 IP TE 90 rt3 - rt8(4)
+ rt4 -
+rt2 TE-IS 100 rt3 - rt5(4)
+ rt4 -
+10.0.255.5/32 IP TE 100 rt3 - rt5(4)
+ rt4 -
+10.0.255.2/32 IP TE 110 rt3 - rt2(4)
+ rt4 -
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.2/32 110 - rt3 20060/18/16020
+ - rt4 16070/18/16020
+ 10.0.255.5/32 100 - rt3 20060/18/16050
+ - rt4 16070/18/16050
+ 10.0.255.8/32 90 - rt3 20060/18
+ - rt4 16070/18
+
+P-space (self):
+ rt3
+ rt4
+ rt6
+ rt7
+
+P-space (rt3):
+ rt3
+ rt6
+
+P-space (rt4):
+ rt4
+ rt7
+
+Q-space:
+ rt2
+ rt5
+ rt8
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt3 TE-IS 20 rt3 - rt1(4)
+rt4 TE-IS 20 rt4 - rt1(4)
+rt6 TE-IS 30 rt3 - rt3(4)
+rt7 TE-IS 30 rt4 - rt4(4)
+2001:db8::3/128 IP6 internal 30 rt3 - rt3(4)
+2001:db8::4/128 IP6 internal 30 rt4 - rt4(4)
+2001:db8::6/128 IP6 internal 40 rt3 - rt6(4)
+2001:db8::7/128 IP6 internal 40 rt4 - rt7(4)
+rt8 TE-IS 80 rt3 - rt6(4)
+ rt4 - rt7(4)
+rt5 TE-IS 90 rt3 - rt8(4)
+ rt4 -
+2001:db8::8/128 IP6 internal 90 rt3 - rt8(4)
+ rt4 -
+rt2 TE-IS 100 rt3 - rt5(4)
+ rt4 -
+2001:db8::5/128 IP6 internal 100 rt3 - rt5(4)
+ rt4 -
+2001:db8::2/128 IP6 internal 110 rt3 - rt2(4)
+ rt4 -
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------------
+ 2001:db8::2/128 110 - rt3 20061/19/16021
+ - rt4 16071/19/16021
+ 2001:db8::5/128 100 - rt3 20061/19/16051
+ - rt4 16071/19/16051
+ 2001:db8::8/128 90 - rt3 20061/19
+ - rt4 16071/19
+
+test# test isis topology 10 root rt1 ti-lfa system-id rt4
+P-space (self):
+ rt2
+ rt3
+ rt5
+ rt6
+ rt8
+
+P-space (rt2):
+ rt2
+ rt5
+ rt8
+
+P-space (rt3):
+ rt3
+ rt6
+
+Q-space:
+ rt4
+ rt7
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 20 rt3 - rt1(4)
+rt5 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+rt6 TE-IS 30 rt3 - rt3(4)
+rt8 TE-IS 30 rt2 - rt5(4)
+10.0.255.3/32 IP TE 30 rt3 - rt3(4)
+10.0.255.5/32 IP TE 30 rt2 - rt5(4)
+10.0.255.6/32 IP TE 40 rt3 - rt6(4)
+10.0.255.8/32 IP TE 40 rt2 - rt8(4)
+rt7 TE-IS 80 rt2 - rt8(4)
+rt4 TE-IS 90 rt2 - rt7(4)
+10.0.255.7/32 IP TE 90 rt2 - rt7(4)
+10.0.255.4/32 IP TE 100 rt2 - rt4(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.4/32 100 - rt2 16080/20/16040
+ 10.0.255.7/32 90 - rt2 16080/20
+
+P-space (self):
+ rt2
+ rt3
+ rt5
+ rt6
+ rt8
+
+P-space (rt2):
+ rt2
+ rt5
+ rt8
+
+P-space (rt3):
+ rt3
+ rt6
+
+Q-space:
+ rt4
+ rt7
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 20 rt3 - rt1(4)
+rt5 TE-IS 20 rt2 - rt2(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+rt6 TE-IS 30 rt3 - rt3(4)
+rt8 TE-IS 30 rt2 - rt5(4)
+2001:db8::3/128 IP6 internal 30 rt3 - rt3(4)
+2001:db8::5/128 IP6 internal 30 rt2 - rt5(4)
+2001:db8::6/128 IP6 internal 40 rt3 - rt6(4)
+2001:db8::8/128 IP6 internal 40 rt2 - rt8(4)
+rt7 TE-IS 80 rt2 - rt8(4)
+rt4 TE-IS 90 rt2 - rt7(4)
+2001:db8::7/128 IP6 internal 90 rt2 - rt7(4)
+2001:db8::4/128 IP6 internal 100 rt2 - rt4(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------------
+ 2001:db8::4/128 100 - rt2 16081/21/16041
+ 2001:db8::7/128 90 - rt2 16081/21
+
+test# test isis topology 11 root rt2 ti-lfa system-id rt4
+P-space (self):
+
+P-space (rt1):
+ rt1
+ rt3
+ rt5
+
+P-space (rt3):
+ rt1
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt1
+ rt3
+ rt4
+ rt5
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt1 TE-IS 50 rt1 - rt2(4)
+rt3 TE-IS 50 rt3 - rt2(4)
+rt2
+rt5 TE-IS 60 rt3 - rt3(4)
+10.0.255.1/32 IP TE 60 rt1 - rt1(4)
+10.0.255.3/32 IP TE 60 rt3 - rt3(4)
+rt4 TE-IS 70 rt3 - rt5(4)
+rt6 TE-IS 70 rt3 - rt5(4)
+10.0.255.5/32 IP TE 70 rt3 - rt5(4)
+10.0.255.4/32 IP TE 80 rt3 - rt4(4)
+10.0.255.6/32 IP TE 80 rt3 - rt6(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 60 - rt1 implicit-null
+ 10.0.255.3/32 60 - rt3 implicit-null
+ 10.0.255.4/32 80 - rt3 16050/16040
+ 10.0.255.5/32 70 - rt3 16050
+ 10.0.255.6/32 80 - rt3 16060
+
+P-space (self):
+
+P-space (rt1):
+ rt1
+ rt3
+ rt5
+
+P-space (rt3):
+ rt1
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt1
+ rt3
+ rt4
+ rt5
+ rt6
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+2001:db8::2/128 IP6 internal 0 rt2(4)
+rt1 TE-IS 50 rt1 - rt2(4)
+rt3 TE-IS 50 rt3 - rt2(4)
+rt2
+rt5 TE-IS 60 rt3 - rt3(4)
+2001:db8::1/128 IP6 internal 60 rt1 - rt1(4)
+2001:db8::3/128 IP6 internal 60 rt3 - rt3(4)
+rt4 TE-IS 70 rt3 - rt5(4)
+rt6 TE-IS 70 rt3 - rt5(4)
+2001:db8::5/128 IP6 internal 70 rt3 - rt5(4)
+2001:db8::4/128 IP6 internal 80 rt3 - rt4(4)
+2001:db8::6/128 IP6 internal 80 rt3 - rt6(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 60 - rt1 implicit-null
+ 2001:db8::3/128 60 - rt3 implicit-null
+ 2001:db8::4/128 80 - rt3 16051/16041
+ 2001:db8::5/128 70 - rt3 16051
+ 2001:db8::6/128 80 - rt3 16061
+
+test# test isis topology 12 root rt1 ti-lfa system-id rt3 ipv4-only
+P-space (self):
+ rt2
+ rt4
+ rt6
+ rt8
+ rt10
+
+P-space (rt2):
+ rt2
+ rt4
+ rt6
+ rt8
+ rt10
+
+Q-space:
+ rt3
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+rt8 TE-IS 40 rt2 - rt6(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+rt10 TE-IS 50 rt2 - rt8(4)
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)
+10.0.255.10/32 IP TE 60 rt2 - rt10(4)
+rt7 TE-IS 140 rt2 - rt8(4)
+rt9 TE-IS 150 rt2 - rt7(4)
+10.0.255.7/32 IP TE 150 rt2 - rt7(4)
+10.0.255.9/32 IP TE 160 rt2 - rt9(4)
+rt5 TE-IS 340 rt2 - rt7(4)
+10.0.255.5/32 IP TE 350 rt2 - rt5(4)
+rt3 TE-IS 740 rt2 - rt5(4)
+10.0.255.3/32 IP TE 750 rt2 - rt3(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.3/32 750 - rt2 16080/17/16/16
+ 10.0.255.5/32 350 - rt2 16080/17/16
+ 10.0.255.7/32 150 - rt2 16080/17
+ 10.0.255.9/32 160 - rt2 16080/17/18
+
+test# test isis topology 13 root rt1 ti-lfa system-id rt3 ipv4-only
+P-space (self):
+ rt2
+
+P-space (rt2):
+ rt2
+ rt4
+
+Q-space:
+ rt3
+ rt4
+ rt5
+ rt6
+ rt7
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+rt3 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+rt5 TE-IS 40 rt2 - rt3(4)
+rt6 TE-IS 40 rt2 - rt3(4)
+10.0.255.3/32 IP TE 40 rt2 - rt3(4)
+rt7 TE-IS 50 rt2 - rt5(4)
+ rt6(4)
+10.0.255.5/32 IP TE 50 rt2 - rt5(4)
+10.0.255.6/32 IP TE 50 rt2 - rt6(4)
+10.0.255.7/32 IP TE 60 rt2 - rt7(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.3/32 40 - rt2 16040/16030
+ 10.0.255.5/32 50 - rt2 16040/16050
+ 10.0.255.6/32 50 - rt2 16040/16060
+ 10.0.255.7/32 60 - rt2 16040/16070
+
+test#
+end.
diff --git a/tests/isisd/test_isis_vertex_queue.c b/tests/isisd/test_isis_vertex_queue.c
new file mode 100644
index 0000000..d2d9f62
--- /dev/null
+++ b/tests/isisd/test_isis_vertex_queue.c
@@ -0,0 +1,106 @@
+#include <zebra.h>
+
+#include "isisd/isis_spf.c"
+
+#include "test_common.h"
+
+static struct isis_vertex **vertices;
+static size_t vertex_count;
+
+static void setup_test_vertices(void)
+{
+ struct isis_spftree t = {
+ };
+ struct prefix_pair p = {
+ };
+ uint8_t node_id[7];
+
+ vertices = XMALLOC(MTYPE_TMP, sizeof(*vertices) * 16);
+
+ p.dest.family = AF_INET;
+ p.dest.prefixlen = 24;
+ inet_pton(AF_INET, "192.168.1.0", &p.dest.u.prefix4);
+ vertices[vertex_count] = isis_vertex_new(&t, &p, VTYPE_IPREACH_TE);
+ vertices[vertex_count]->d_N = 20;
+ vertex_count++;
+
+ p.dest.family = AF_INET;
+ p.dest.prefixlen = 24;
+ inet_pton(AF_INET, "192.168.2.0", &p.dest.u.prefix4);
+ vertices[vertex_count] = isis_vertex_new(&t, &p, VTYPE_IPREACH_TE);
+ vertices[vertex_count]->d_N = 20;
+ vertex_count++;
+
+ memset(node_id, 0, sizeof(node_id));
+ node_id[6] = 1;
+ vertices[vertex_count] = isis_vertex_new(&t, node_id,
+ VTYPE_PSEUDO_TE_IS);
+ vertices[vertex_count]->d_N = 15;
+ vertex_count++;
+
+ memset(node_id, 0, sizeof(node_id));
+ node_id[5] = 2;
+ vertices[vertex_count] = isis_vertex_new(&t, node_id,
+ VTYPE_NONPSEUDO_TE_IS);
+ vertices[vertex_count]->d_N = 15;
+ vertex_count++;
+
+ p.dest.family = AF_INET;
+ p.dest.prefixlen = 24;
+ inet_pton(AF_INET, "192.168.3.0", &p.dest.u.prefix4);
+ vertices[vertex_count] = isis_vertex_new(&t, &p, VTYPE_IPREACH_TE);
+ vertices[vertex_count]->d_N = 20;
+ vertex_count++;
+};
+
+static void cleanup_test_vertices(void)
+{
+ for (size_t i = 0; i < vertex_count; i++)
+ isis_vertex_del(vertices[i]);
+ XFREE(MTYPE_TMP, vertices);
+ vertex_count = 0;
+}
+
+static void test_ordered(void)
+{
+ struct isis_vertex_queue q;
+
+ isis_vertex_queue_init(&q, NULL, true);
+ for (size_t i = 0; i < vertex_count; i++)
+ isis_vertex_queue_insert(&q, vertices[i]);
+
+ assert(isis_vertex_queue_count(&q) == vertex_count);
+
+ for (size_t i = 0; i < vertex_count; i++) {
+ assert(isis_find_vertex(&q, &vertices[i]->N, vertices[i]->type) == vertices[i]);
+ }
+
+ assert(isis_vertex_queue_pop(&q) == vertices[2]);
+ assert(isis_find_vertex(&q, &vertices[2]->N, vertices[2]->type) == NULL);
+
+ assert(isis_vertex_queue_pop(&q) == vertices[3]);
+ assert(isis_find_vertex(&q, &vertices[3]->N, vertices[3]->type) == NULL);
+
+ assert(isis_vertex_queue_pop(&q) == vertices[0]);
+ assert(isis_find_vertex(&q, &vertices[0]->N, vertices[0]->type) == NULL);
+
+ assert(isis_vertex_queue_pop(&q) == vertices[1]);
+ assert(isis_find_vertex(&q, &vertices[1]->N, vertices[1]->type) == NULL);
+
+ isis_vertex_queue_delete(&q, vertices[4]);
+ assert(isis_find_vertex(&q, &vertices[4]->N, vertices[4]->type) == NULL);
+
+ assert(isis_vertex_queue_count(&q) == 0);
+ assert(isis_vertex_queue_pop(&q) == NULL);
+
+ isis_vertex_queue_free(&q);
+}
+
+int main(int argc, char **argv)
+{
+ setup_test_vertices();
+ test_ordered();
+ cleanup_test_vertices();
+
+ return 0;
+}
diff --git a/tests/isisd/test_isis_vertex_queue.py b/tests/isisd/test_isis_vertex_queue.py
new file mode 100644
index 0000000..b9d2fc5
--- /dev/null
+++ b/tests/isisd/test_isis_vertex_queue.py
@@ -0,0 +1,8 @@
+import frrtest
+
+
+class TestIsisVertexQueue(frrtest.TestMultiOut):
+ program = "./test_isis_vertex_queue"
+
+
+TestIsisVertexQueue.exit_cleanly()
diff --git a/tests/isisd/test_topologies.c b/tests/isisd/test_topologies.c
new file mode 100644
index 0000000..a0ac768
--- /dev/null
+++ b/tests/isisd/test_topologies.c
@@ -0,0 +1,3474 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#include <zebra.h>
+
+#include "isisd/isisd.h"
+
+#include "test_common.h"
+
+/*
+ * clang-format off
+ *
+ * All topologies have the following properties:
+ * - The System-ID is 0000.0000.000X, where X is the node number (in hex);
+ * - The Router-ID is 10.0.255.X, where X is the node number;
+ * - The default link metric is 10;
+ * - When SR is enabled, Adj-SIDs and Prefix-SIDs are generated automatically;
+ * - When SR is enabled, the default SRGB is [16000-23999] (can be overridden).
+ *
+ * Test topology 1:
+ * ================
+ *
+ * +---------+
+ * | |
+ * | RT1 |
+ * +----------+ +----------+
+ * | | | |
+ * | +---------+ |
+ * | |
+ * | |
+ * | |
+ * +----+----+ +----+----+
+ * | | | |
+ * | RT2 | | RT3 |
+ * | | | |
+ * | | | |
+ * +----+----+ +----+----+
+ * | |
+ * | |
+ * | |
+ * +---+-+---+ +----+----+
+ * | | | |
+ * | RT4 | | RT5 |
+ * | | | |
+ * | | | |
+ * +----+----+ +----+----+
+ * | |
+ * | |
+ * | |
+ * | +---------+ |
+ * | | | |
+ * | | RT6 | |
+ * +----------+ +----------+
+ * | |
+ * +---------+
+ *
+ * Test topology 2:
+ * ================
+ *
+ * +---------+
+ * | |
+ * | RT1 |
+ * +----------+ +----------+
+ * | | | |
+ * | +----+----+ |
+ * | | |
+ * 15 | | | 30
+ * | | |
+ * +----+----+ | +----+----+
+ * | | | | |
+ * | RT2 | | | RT3 |
+ * | | | | |
+ * | | | | |
+ * +----+----+ | +----+----+
+ * | | |
+ * 40 | | | 40
+ * | | |
+ * +----+----+ | +----+----+
+ * | | | | |
+ * | RT4 | | | RT5 |
+ * | +----------+----------+ |
+ * | | | |
+ * +----+----+ +----+----+
+ * | |
+ * | |
+ * | |
+ * | +---------+ |
+ * | | | |
+ * | | RT6 | |
+ * +----------+ +----------+
+ * | |
+ * +---------+
+ *
+ * Test topology 3:
+ * ================
+ *
+ * +---------+
+ * | |
+ * | RT1 |
+ * +----------+ +----------+
+ * | | | |
+ * | +---------+ |
+ * | |
+ * | |
+ * | |
+ * +----+----+ +----+----+
+ * | | | |
+ * | RT2 | | RT3 |
+ * | +---------------------+ |
+ * | | | |
+ * +----+----+ +----+----+
+ * | |
+ * | | 30
+ * | |
+ * +----+----+ +----+----+
+ * | | | |
+ * | RT4 | | RT5 |
+ * | +---------------------+ |
+ * | | | |
+ * +----+----+ +----+----+
+ * | |
+ * | |
+ * | |
+ * | +---------+ |
+ * | | | |
+ * | | RT6 | |
+ * +----------+ +----------+
+ * | |
+ * +---------+
+ *
+ * Test topology 4:
+ * ================
+ *
+ * +---------+ +---------+
+ * | | | |
+ * | RT1 | | RT2 |
+ * | +---------------------+ |
+ * | | | |
+ * +---+-----+ +------+--+
+ * | |
+ * | |
+ * | |
+ * +---+-----+ +------+--+
+ * | | | |
+ * | RT3 | | RT4 |
+ * | | | |
+ * | | | |
+ * +---+-----+ +------+--+
+ * |^ |
+ * ||200 |
+ * | |
+ * +---+-----+ +------+--+
+ * | | | |
+ * | RT5 | 50 | RT6 |
+ * | +---------------------+ |
+ * | | | |
+ * +---+-----+ +------+--+
+ * | |
+ * | |
+ * | |
+ * +---+-----+ +------+--+
+ * | | | |
+ * | RT7 | | RT8 |
+ * | | | |
+ * | | | |
+ * +---------+ +---------+
+ *
+ * Test topology 5:
+ * ================
+ *
+ * +---------+ +---------+
+ * | | | |
+ * | RT1 | | RT2 |
+ * | +---------------------+ |
+ * | | | |
+ * +---+-----+ +------+--+
+ * | |
+ * | |
+ * | |
+ * +---+-----+ +------+--+
+ * | | | |
+ * | RT3 | | RT4 |
+ * | | | |
+ * | | | |
+ * +---+-----+ +------+--+
+ * | |
+ * | |
+ * | |
+ * +---+-----+ +------+--+
+ * | | | |
+ * | RT5 | | RT6 |
+ * | | | |
+ * | | | |
+ * +---+-----+ +------+--+
+ * | |
+ * | |
+ * | |
+ * +---+-----+ +------+--+
+ * | | | |
+ * | RT7 | | RT8 |
+ * | +---------------------+ |
+ * | | | |
+ * +---------+ +---------+
+ *
+ * Test topology 6:
+ * ================
+ *
+ * +---------+ +---------+
+ * | | | |
+ * | RT1 | | RT2 |
+ * | +---------------------+ |
+ * | | | |
+ * +---+-----+ +------+--+
+ * | |
+ * | |
+ * | |
+ * +---+-----+ +------+--+
+ * | | | |
+ * | RT3 | | RT4 |
+ * | +---------------------+ |
+ * | | | |
+ * +---------+ +------+--+
+ * |
+ * |
+ * |
+ * +---------+ +------+--+
+ * | | | |
+ * | RT5 | | RT6 |
+ * | +---------------------+ |
+ * | | | |
+ * +---+-----+ +------+--+
+ * | |
+ * | |
+ * | |
+ * +---+-----+ +------+--+
+ * | | | |
+ * | RT7 | | RT8 |
+ * | +---------------------+ |
+ * | | | |
+ * +---------+ +---------+
+ *
+ * Test topology 7:
+ * ================
+ *
+ * +---------+ +---------+ +---------+
+ * | | | | | |
+ * | RT1 | 40 | RT2 | | RT3 |
+ * | +---------------------+ +---------------------+ |
+ * | | | | | |
+ * +---+-----+ +----+----+ +------+--+
+ * | | |
+ * | | |
+ * | | |
+ * +---+-----+ +----+----+ +------+--+
+ * | | | | | |
+ * | RT4 | | RT5 | | RT6 |
+ * | +---------------------+ +---------------------+ |
+ * | | | | | |
+ * +---+-----+ +----+----+ +------+--+
+ * | | |
+ * | | | 30
+ * | | |
+ * +---+-----+ +----+----+ +------+--+
+ * | | | | | |
+ * | RT7 | | RT8 | | RT9 |
+ * | +---------------------+ +---------------------+ |
+ * | | | | | |
+ * +---+-----+ +----+----+ +------+--+
+ * | | |
+ * | 20 | |
+ * | | |
+ * +---+-----+ +----+----+ +------+--+
+ * | | | | | |
+ * | RT10 | | RT11 | | RT12 |
+ * | +---------------------+ +---------------------+ |
+ * | | | | | |
+ * +---------+ +---------+ +---------+
+ *
+ * Test topology 8:
+ * ================
+ *
+ * +---------+ +---------+ +---------+
+ * | | | | | |
+ * | RT1 | | RT2 | | RT3 |
+ * | +---------------------+ +---------------------+ |
+ * | | | | | |
+ * +---+-----+ +----+----+ +------+--+
+ * | | |
+ * | | |
+ * | | |
+ * +---+-----+ +----+----+ +------+--+
+ * | | | | | |
+ * | RT4 | | RT5 | | RT6 |
+ * | | | +---------------------+ |
+ * | | | | | |
+ * +---+-----+ +----+----+ +---------+
+ * | |
+ * | |
+ * | |
+ * +---+-----+ +----+----+ +---------+
+ * | | | | | |
+ * | RT7 | | RT8 | | RT9 |
+ * | | | +---------------------+ |
+ * | | | | | |
+ * +---+-----+ +----+----+ +------+--+
+ * | | |
+ * | | |
+ * | | |
+ * +---+-----+ +----+----+ +------+--+
+ * | | | | | |
+ * | RT10 | | RT11 | | RT12 |
+ * | +---------------------+ +---------------------+ |
+ * | | 30 | | | |
+ * +---------+ +---------+ +---------+
+ *
+ * Test topology 9:
+ * ================
+ *
+ * +---------+
+ * | |
+ * | RT1 |
+ * +----------+ +----------+
+ * | | | |
+ * | +---------+ |
+ * | |
+ * | |
+ * | |
+ * +----+----+ +----+----+
+ * | | | |
+ * | RT2 | | RT3 |
+ * | | | |
+ * | | | |
+ * +----+----+ +------+--+
+ * | |
+ * | |
+ * | |100
+ * | +---------+ |
+ * | | | |
+ * +----------+ RT4 +------------+
+ * +----------------| |----------------+
+ * | +-+ +--+ |
+ * | | +---------+ | |
+ * | | | |
+ * | |30 |30 |30
+ * | | | |
+ * +----+----+ +----+----+ +----+----+ +----+----+
+ * | | | | | | | |
+ * | RT5 | | RT6 | | RT7 | | RT8 |
+ * | | | | | | | |
+ * | | | | | | | |
+ * +----+----+ +----+----+ +----+----+ +----+----+
+ * | | | |
+ * | | | |
+ * | | | |
+ * | | +---------+ | |
+ * | +-+ +--+ |
+ * +----------------+ RT9 +----------------+
+ * | |
+ * | |
+ * +---------+
+ *
+ * Test topology 10:
+ * ================
+ *
+ * +---------+
+ * | |
+ * | RT1 |
+ * +----------+ +----------+
+ * | | | |
+ * | +----+----+ |
+ * | | |
+ * | |20 |20
+ * | | |
+ * +----+----+ +----+----+ +----+----+
+ * | | | | | |
+ * | RT2 | | RT3 | | RT4 |
+ * | | | | | |
+ * | | | | | |
+ * +----+----+ +----+----+ +----+----+
+ * | | |
+ * | | |
+ * | | |
+ * +----+----+ +----+----+ +----+----+
+ * | | | | | |
+ * | RT5 | | RT6 | | RT7 |
+ * | | | | | |
+ * | | | | | |
+ * +----+----+ +----+----+ +----+----+
+ * | | |
+ * | |50 |50
+ * | | |
+ * | +----+----+ |
+ * | | | |
+ * +----------+ RT8 +----------+
+ * | |
+ * | |
+ * +---------+
+ *
+ * Test topology 11:
+ * ================
+ *
+ * +---------+
+ * | |
+ * | RT1 |
+ * | |
+ * | |
+ * +----+----+
+ * |
+ * |
+ * |
+ * +---------+ | +---------+
+ * | | | | |
+ * | RT2 |50 | | RT3 |
+ * | +----------+----------+ |
+ * | | | |
+ * +----+----+ +----+----+
+ * | |
+ * | |
+ * | |
+ * +----+----+ +----+----+
+ * | | | |
+ * | RT4 | | RT5 |
+ * | +---------------------+ |
+ * | | | |
+ * +----+----+ +----+----+
+ * | |
+ * | |
+ * | |
+ * | +---------+ |
+ * | | | |
+ * | | RT6 | |
+ * +----------+ +----------+
+ * | |
+ * +---------+
+ *
+ * Test topology 12:
+ * ================
+ *
+ * +---------+ +---------+
+ * | | | |
+ * | RT1 | | RT2 |
+ * | +---------------------+ |
+ * | | | |
+ * +---+-----+ +------+--+
+ * | |
+ * | |
+ * | |
+ * +---+-----+ +------+--+
+ * | | | |
+ * | RT3 | | RT4 |
+ * | | | |
+ * | | | |
+ * +---+-----+ +------+--+
+ * |^ |
+ * |400 |
+ * | |
+ * +---+-----+ +------+--+
+ * | | | |
+ * | RT5 | | RT6 |
+ * | | | |
+ * | | | |
+ * +---+-----+ +------+--+
+ * |^ |
+ * |200 |
+ * | |
+ * +---+-----+ +------+--+
+ * | | | |
+ * | RT7 | | RT8 |
+ * | +---------------------+ |
+ * | | 100 | |
+ * +---+-----+ +------+--+
+ * | |
+ * | |
+ * | |
+ * +---+-----+ +------+--+
+ * | | | |
+ * | RT9 | | RT10 |
+ * | | | |
+ * | | | |
+ * +---------+ +---------+
+ *
+ * Test topology 13:
+ * ================
+ *
+ * +---------+ +---------+
+ * | | | |
+ * | RT1 | | RT2 |
+ * | +---------------------+ |
+ * | | | |
+ * +---+-----+ +----+----+
+ * | |
+ * | |
+ * | |
+ * | +----+----+
+ * | | |
+ * | +----------+ RT4 |
+ * | | | |
+ * +---+-----+ | | |
+ * | | | +----+----+
+ * | RT3 +----------+ |
+ * | +----------+ |100
+ * | | | |
+ * +---+-----+ | +----+----+
+ * | | | |
+ * | | | RT5 |
+ * | +----------+ |
+ * | | |
+ * | +----+----+
+ * | |
+ * | |
+ * | |
+ * +---+-----+ +----+----+
+ * | | | |
+ * | RT6 | | RT7 |
+ * | +---------------------+ |
+ * | | | |
+ * +---------+ +---------+
+
+ * Test topology 14:
+ * =================
+ *
+ * +---------+ +---------+
+ * | | | |
+ * | RT1 | | RT2 |
+ * | +--------------+ |
+ * | | | |
+ * +----+----+ +----+----+
+ * | |
+ * | |
+ * | |
+ * | +----+----+
+ * | | |
+ * | | RT3 |
+ * +-------------------+ |
+ * | | |
+ * | +----+----+
+ * | |
+ * | |50
+ * | |
+ * +----+----+ +----+----+
+ * | | | |
+ * | RT4 | | RT5 |
+ * | +--------------+ |
+ * | | | |
+ * +---------+ +---------+
+ */
+
+struct isis_topology test_topologies[] = {
+ {
+ .number = 1,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ "2001:db8::1/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ "2001:db8::2/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ "2001:db8::3/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ "2001:db8::4/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ "2001:db8::5/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt6",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.6",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.6/32",
+ "2001:db8::6/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ },
+ },
+ {
+ .number = 2,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ "2001:db8::1/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .pseudonode_id = 1,
+ .metric = 10,
+ },
+ {
+ .hostname = "rt2",
+ .metric = 15,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 30,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ "2001:db8::2/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 15,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 40,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ "2001:db8::3/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 30,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 40,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ "2001:db8::4/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .pseudonode_id = 1,
+ .metric = 10,
+ },
+ {
+ .hostname = "rt2",
+ .metric = 40,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ "2001:db8::5/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .pseudonode_id = 1,
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 40,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt6",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.6",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.6/32",
+ "2001:db8::6/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .pseudonode_id = 1,
+ .level = IS_LEVEL_1,
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 0,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 0,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 0,
+ },
+ },
+ },
+ },
+ },
+ {
+ .number = 3,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 30,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt3",
+ .metric = 30,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt6",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.6",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.6/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ },
+ },
+ {
+ .number = 4,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt3",
+ .metric = 200,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 50,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt6",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.6",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.6/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 50,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt7",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.7",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.7/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt8",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.8",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.8/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ },
+ },
+ {
+ .number = 5,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt6",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.6",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.6/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt7",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.7",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.7/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt8",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.8",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.8/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ },
+ },
+ {
+ .number = 6,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt6",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.6",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.6/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt7",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.7",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.7/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt8",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.8",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.8/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ },
+ },
+ {
+ .number = 7,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 40,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 40,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt6",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.6",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.6/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt9",
+ .metric = 30,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt7",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.7",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.7/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt10",
+ .metric = 20,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt8",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.8",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.8/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt9",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt11",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt9",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x09},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.9",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.9/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt6",
+ .metric = 30,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt12",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt10",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0a},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.10",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.10/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt7",
+ .metric = 20,
+ },
+ {
+ .hostname = "rt11",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt11",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0b},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.11",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.11/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt10",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt12",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt12",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0c},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.12",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.12/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt9",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt11",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ },
+ },
+ {
+ .number = 8,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt6",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.6",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.6/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt7",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.7",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.7/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt10",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt8",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.8",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.8/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt9",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt11",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt9",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x09},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.9",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.9/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt12",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt10",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0a},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.10",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.10/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt11",
+ .metric = 30,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt11",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0b},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.11",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.11/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt10",
+ .metric = 30,
+ },
+ {
+ .hostname = "rt12",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt12",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0c},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.12",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.12/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt9",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt11",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ },
+ },
+ {
+ .number = 9,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ "2001:db8::1/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ "2001:db8::2/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ "2001:db8::3/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 100,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ "2001:db8::4/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 100,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 30,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 30,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 30,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ "2001:db8::5/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt9",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt6",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.6",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.6/32",
+ "2001:db8::6/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 30,
+ },
+ {
+ .hostname = "rt9",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt7",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.7",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.7/32",
+ "2001:db8::7/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 30,
+ },
+ {
+ .hostname = "rt9",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt8",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.8",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.8/32",
+ "2001:db8::8/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 30,
+ },
+ {
+ .hostname = "rt9",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt9",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x09},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.9",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.9/32",
+ "2001:db8::9/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ },
+ },
+ {
+ .number = 10,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ "2001:db8::1/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 20,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 20,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ "2001:db8::2/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .srgb = {
+ .lower_bound = 20000,
+ .range_size = 8000,
+ },
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ "2001:db8::3/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 20,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ "2001:db8::4/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 20,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ "2001:db8::5/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt6",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.6",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.6/32",
+ "2001:db8::6/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 50,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt7",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.7",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.7/32",
+ "2001:db8::7/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 50,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt8",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.8",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.8/32",
+ "2001:db8::8/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 50,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 50,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ },
+ },
+ {
+ .number = 11,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ "2001:db8::1/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .pseudonode_id = 1,
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ "2001:db8::2/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .pseudonode_id = 1,
+ .metric = 50,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ "2001:db8::3/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .pseudonode_id = 1,
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ "2001:db8::4/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ "2001:db8::5/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt6",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.6",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.6/32",
+ "2001:db8::6/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .pseudonode_id = 1,
+ .level = IS_LEVEL_1,
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 0,
+ },
+ {
+ .hostname = "rt2",
+ .metric = 0,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 0,
+ },
+ },
+ },
+ },
+ },
+ {
+ .number = 12,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt3",
+ .metric = 400,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt6",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.6",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.6/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt7",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.7",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.7/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt5",
+ .metric = 200,
+ },
+ {
+ .hostname = "rt8",
+ .metric = 100,
+ },
+ {
+ .hostname = "rt9",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt8",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.8",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.8/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 100,
+ },
+ {
+ .hostname = "rt10",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt9",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x09},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.9",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.9/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt10",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0a},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.10",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.10/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt8",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ },
+ },
+ {
+ .number = 13,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 100,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 100,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt6",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.6",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.6/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt7",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ {
+ .hostname = "rt7",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.7",
+ .protocols = {
+ .ipv4 = true,
+ },
+ .networks = {
+ "10.0.255.7/32",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt6",
+ .metric = 10,
+ },
+ },
+ .flags = F_ISIS_TEST_NODE_SR,
+ },
+ },
+ },
+ {
+ .number = 14,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ "2001:db8::1/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .pseudonode_id = 1,
+ .metric = 10,
+ },
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ },
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ "2001:db8::2/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 20,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ },
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ "2001:db8::3/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .pseudonode_id = 1,
+ .metric = 10,
+ },
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 50,
+ },
+ },
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ "2001:db8::4/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .pseudonode_id = 1,
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ "2001:db8::5/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 50,
+ },
+ },
+ },
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .pseudonode_id = 1,
+ .level = IS_LEVEL_1,
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 0,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 0,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 0,
+ },
+ },
+ },
+ },
+ },
+ {
+ /* sentinel */
+ },
+};
diff --git a/tests/lib/cli/.gitignore b/tests/lib/cli/.gitignore
new file mode 100644
index 0000000..682e95f
--- /dev/null
+++ b/tests/lib/cli/.gitignore
@@ -0,0 +1 @@
+/test_cli.refout
diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c
new file mode 100644
index 0000000..e0981b9
--- /dev/null
+++ b/tests/lib/cli/common_cli.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * generic CLI test helper functions
+ *
+ * Copyright (C) 2015 by David Lamparter,
+ * for Open Source Routing / NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#include "frrevent.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "lib_vty.h"
+#include "log.h"
+
+#include "common_cli.h"
+
+struct event_loop *master;
+
+int dump_args(struct vty *vty, const char *descr, int argc,
+ struct cmd_token *argv[])
+{
+ int i;
+ vty_out(vty, "%s with %d args.\n", descr, argc);
+ for (i = 0; i < argc; i++) {
+ vty_out(vty, "[%02d] %s@%s: %s\n", i, argv[i]->text,
+ argv[i]->varname, argv[i]->arg);
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void vty_do_exit(int isexit)
+{
+ printf("\nend.\n");
+ cmd_terminate();
+ vty_terminate();
+ nb_terminate();
+ yang_terminate();
+ event_master_free(master);
+
+ log_memstats(stderr, "testcli");
+ if (!isexit)
+ exit(0);
+}
+
+const struct frr_yang_module_info *const *test_yang_modules = NULL;
+int test_log_prio = ZLOG_DISABLED;
+
+/* main routine. */
+int main(int argc, char **argv)
+{
+ struct event thread;
+ size_t yangcount;
+
+ /* Set umask before anything for security */
+ umask(0027);
+
+ /* master init. */
+ master = event_master_create(NULL);
+
+ zlog_aux_init("NONE: ", test_log_prio);
+
+ /* Library inits. */
+ cmd_init(1);
+ cmd_hostname_set("test");
+ cmd_domainname_set("test.domain");
+
+ vty_init(master, false);
+ lib_cmd_init();
+
+ for (yangcount = 0; test_yang_modules && test_yang_modules[yangcount];
+ yangcount++)
+ ;
+ nb_init(master, test_yang_modules, yangcount, false);
+
+ test_init(argc, argv);
+
+ vty_stdio(vty_do_exit);
+
+ /* Fetch next active thread. */
+ while (event_fetch(master, &thread))
+ event_call(&thread);
+
+ /* Not reached. */
+ exit(0);
+}
diff --git a/tests/lib/cli/common_cli.h b/tests/lib/cli/common_cli.h
new file mode 100644
index 0000000..4a1de94
--- /dev/null
+++ b/tests/lib/cli/common_cli.h
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * generic CLI test helper functions
+ *
+ * Copyright (C) 2015 by David Lamparter,
+ * for Open Source Routing / NetDEF, Inc.
+ */
+
+#ifndef _COMMON_CLI_H
+#define _COMMON_CLI_H
+
+#include "zebra.h"
+#include "vty.h"
+#include "command.h"
+#include "northbound.h"
+
+extern const struct frr_yang_module_info *const *test_yang_modules;
+
+/* function to be implemented by test */
+extern void test_init(int argc, char **argv);
+
+/* functions provided by common cli
+ * (includes main())
+ */
+extern struct event_loop *master;
+
+extern int test_log_prio;
+
+extern int dump_args(struct vty *vty, const char *descr, int argc,
+ struct cmd_token *argv[]);
+
+#define DUMMY_HELPSTR \
+ "00\n01\n02\n03\n04\n05\n06\n07\n08\n09\n" \
+ "10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n" \
+ "20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n"
+#define DUMMY_DEFUN(name, cmdstr) \
+ DEFUN(name, name##_cmd, cmdstr, DUMMY_HELPSTR) \
+ { \
+ return dump_args(vty, #name, argc, argv); \
+ }
+
+#endif /* _COMMON_CLI_H */
diff --git a/tests/lib/cli/test_cli.c b/tests/lib/cli/test_cli.c
new file mode 100644
index 0000000..0314735
--- /dev/null
+++ b/tests/lib/cli/test_cli.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * CLI/command dummy handling tester
+ *
+ * Copyright (C) 2015 by David Lamparter,
+ * for Open Source Routing / NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "common_cli.h"
+
+DUMMY_DEFUN(cmd0, "arg ipv4 A.B.C.D");
+DUMMY_DEFUN(cmd1, "arg ipv4m A.B.C.D/M");
+DUMMY_DEFUN(cmd2, "arg ipv6 X:X::X:X$foo");
+DUMMY_DEFUN(cmd3, "arg ipv6m X:X::X:X/M");
+DUMMY_DEFUN(cmd4, "arg range (5-15)");
+DUMMY_DEFUN(cmd5, "pat a < a|b>");
+DUMMY_DEFUN(cmd6, "pat b <a|b A.B.C.D$bar>");
+DUMMY_DEFUN(cmd7, "pat c <a | b|c> A.B.C.D");
+DUMMY_DEFUN(cmd8, "pat d { foo A.B.C.D$foo|bar X:X::X:X$bar| baz } [final]");
+DUMMY_DEFUN(cmd9, "pat e [ WORD ]");
+DUMMY_DEFUN(cmd10, "pat f [key]");
+DUMMY_DEFUN(cmd11, "alt a WORD");
+DUMMY_DEFUN(cmd12, "alt a A.B.C.D");
+DUMMY_DEFUN(cmd13, "alt a X:X::X:X");
+DUMMY_DEFUN(cmd14,
+ "pat g { foo A.B.C.D$foo|foo|bar X:X::X:X$bar| baz } [final]");
+DUMMY_DEFUN(cmd15, "no pat g ![ WORD ]");
+DUMMY_DEFUN(cmd16, "[no] pat h {foo ![A.B.C.D$foo]|bar X:X::X:X$bar} final");
+
+#include "tests/lib/cli/test_cli_clippy.c"
+
+DEFPY(magic_test, magic_test_cmd,
+ "magic (0-100) {ipv4net A.B.C.D/M|X:X::X:X$ipv6}",
+ "1\n2\n3\n4\n5\n")
+{
+ vty_out(vty, "def: %s\n", self->string);
+ vty_out(vty, "num: %ld\n", magic);
+ vty_out(vty, "ipv4: %pFX\n", ipv4net);
+ vty_out(vty, "ipv6: %pI6\n", &ipv6);
+ return CMD_SUCCESS;
+}
+
+void test_init(int argc, char **argv)
+{
+ size_t repeat = argc > 1 ? strtoul(argv[1], NULL, 0) : 223;
+
+ install_element(ENABLE_NODE, &cmd0_cmd);
+ install_element(ENABLE_NODE, &cmd1_cmd);
+ install_element(ENABLE_NODE, &cmd2_cmd);
+ install_element(ENABLE_NODE, &cmd3_cmd);
+ install_element(ENABLE_NODE, &cmd4_cmd);
+ install_element(ENABLE_NODE, &cmd5_cmd);
+ install_element(ENABLE_NODE, &cmd6_cmd);
+ install_element(ENABLE_NODE, &cmd7_cmd);
+ install_element(ENABLE_NODE, &cmd8_cmd);
+ install_element(ENABLE_NODE, &cmd9_cmd);
+ install_element(ENABLE_NODE, &cmd10_cmd);
+ install_element(ENABLE_NODE, &cmd11_cmd);
+ install_element(ENABLE_NODE, &cmd12_cmd);
+ install_element(ENABLE_NODE, &cmd13_cmd);
+ for (size_t i = 0; i < repeat; i++) {
+ uninstall_element(ENABLE_NODE, &cmd5_cmd);
+ install_element(ENABLE_NODE, &cmd5_cmd);
+ }
+ for (size_t i = 0; i < repeat; i++) {
+ uninstall_element(ENABLE_NODE, &cmd13_cmd);
+ install_element(ENABLE_NODE, &cmd13_cmd);
+ }
+ install_element(ENABLE_NODE, &cmd14_cmd);
+ install_element(ENABLE_NODE, &cmd15_cmd);
+ install_element(ENABLE_NODE, &cmd16_cmd);
+ install_element(ENABLE_NODE, &magic_test_cmd);
+}
diff --git a/tests/lib/cli/test_cli.in b/tests/lib/cli/test_cli.in
new file mode 100644
index 0000000..bd685a6
--- /dev/null
+++ b/tests/lib/cli/test_cli.in
@@ -0,0 +1,105 @@
+echo this is a test message
+echo foo bla ? baz
+echo
+
+arg ipv4 1.2.3.4
+arg ipv4 1.2.?3.4
+arg ipv4 1.2.3
+arg ipv4 1.2.3.4.5
+arg ipv4 1.a.3.4
+arg ipv4 blah
+
+arg ipv4m 1.2.3.0/24
+arg ipv4m 1.2.?3.0/24
+arg ipv4m 1.2.3/9
+arg ipv4m 1.2.3.4.5/6
+arg ipv4m 1.a.3.4
+arg ipv4m blah
+arg ipv4m 1.2.3.0/999
+arg ipv4m 1.2.3.0/a9
+arg ipv4m 1.2.3.0/9a
+
+arg ipv6 de4d:b33f::cafe
+arg ipv6 de4d:b3?3f::caf?e
+arg ipv6 de4d:b3 3f::caf?e
+arg ipv6 de4d:b33f:z::cafe
+arg ipv6 de4d:b33f:cafe:
+arg ipv6 ::
+arg ipv6 ::/
+arg ipv6 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
+arg ipv6 12::34::56
+arg ipv6m dead:beef:cafe::/64
+arg ipv6m dead:be?ef:cafe:?:/64
+
+arg range 4
+arg range 5
+arg range 9?
+arg range 15
+arg range 16
+arg range -1
+arg range 99999999999999999999999999999999999999999
+
+arg ?
+
+pa
+pat
+
+pat a
+pat a a
+pat a ?b
+pat a c?
+pat a a x
+
+pat c a
+pat c a 1.2.3.4
+pat c b 2.3.4
+pat c c ?x
+
+pat d
+pat d
+pat d foo 1.2.3.4
+pat d foo
+pat d noooo
+pat d bar 1::2
+pat d bar 1::2 foo 3.4.5.6
+pat d ba?z
+pat d foo 3.4.5.6 baz
+
+pat e
+pat e f
+pat e f g
+pat e 1.2.3.4
+
+pat f
+pat f foo
+pat f key
+
+no pat g
+no pat g test
+no pat g test more
+
+pat h foo ?1.2.3.4 final
+no pat h foo ?1.2.3.4 final
+pat h foo final
+no pat h foo final
+pat h bar final
+no pat h bar final
+pat h bar 1::2 final
+no pat h bar 1::2 final
+pat h bar 1::2 foo final
+no pat h bar 1::2 foo final
+pat h bar 1::2 foo 1.2.3.4 final
+no pat h bar 1::2 foo 1.2.3.4 final
+
+alt a a?b
+alt a 1 .2?.3.4
+alt a 1 :2? ::?3
+
+conf t
+do pat d baz
+exit
+
+show run
+conf t
+hostname foohost
+do show run
diff --git a/tests/lib/cli/test_cli.py b/tests/lib/cli/test_cli.py
new file mode 100644
index 0000000..6fdd6fa
--- /dev/null
+++ b/tests/lib/cli/test_cli.py
@@ -0,0 +1,6 @@
+import frrtest
+
+
+class TestCli(frrtest.TestRefOut):
+ program = "./test_cli"
+ built_refout = True
diff --git a/tests/lib/cli/test_cli.refout.in b/tests/lib/cli/test_cli.refout.in
new file mode 100644
index 0000000..8436581
--- /dev/null
+++ b/tests/lib/cli/test_cli.refout.in
@@ -0,0 +1,431 @@
+test# echo this is a test message
+this is a test message
+test# echo foo bla
+% There is no matched command.
+test# echo foo bla baz
+foo bla baz
+test# echo
+% Command incomplete.
+test#
+test# arg ipv4 1.2.3.4
+cmd0 with 3 args.
+[00] arg@(null): arg
+[01] ipv4@(null): ipv4
+[02] A.B.C.D@ipv4: 1.2.3.4
+test# arg ipv4 1.2.
+ A.B.C.D 02
+test# arg ipv4 1.2.3.4
+cmd0 with 3 args.
+[00] arg@(null): arg
+[01] ipv4@(null): ipv4
+[02] A.B.C.D@ipv4: 1.2.3.4
+test# arg ipv4 1.2.3
+% [NONE] Unknown command: arg ipv4 1.2.3
+test# arg ipv4 1.2.3.4.5
+% [NONE] Unknown command: arg ipv4 1.2.3.4.5
+test# arg ipv4 1.a.3.4
+% [NONE] Unknown command: arg ipv4 1.a.3.4
+test# arg ipv4 blah
+% [NONE] Unknown command: arg ipv4 blah
+test#
+test# arg ipv4m 1.2.3.0/24
+cmd1 with 3 args.
+[00] arg@(null): arg
+[01] ipv4m@(null): ipv4m
+[02] A.B.C.D/M@ipv4m: 1.2.3.0/24
+test# arg ipv4m 1.2.
+ A.B.C.D/M 02
+test# arg ipv4m 1.2.3.0/24
+cmd1 with 3 args.
+[00] arg@(null): arg
+[01] ipv4m@(null): ipv4m
+[02] A.B.C.D/M@ipv4m: 1.2.3.0/24
+test# arg ipv4m 1.2.3/9
+% [NONE] Unknown command: arg ipv4m 1.2.3/9
+test# arg ipv4m 1.2.3.4.5/6
+% [NONE] Unknown command: arg ipv4m 1.2.3.4.5/6
+test# arg ipv4m 1.a.3.4
+% [NONE] Unknown command: arg ipv4m 1.a.3.4
+test# arg ipv4m blah
+% [NONE] Unknown command: arg ipv4m blah
+test# arg ipv4m 1.2.3.0/999
+% [NONE] Unknown command: arg ipv4m 1.2.3.0/999
+test# arg ipv4m 1.2.3.0/a9
+% [NONE] Unknown command: arg ipv4m 1.2.3.0/a9
+test# arg ipv4m 1.2.3.0/9a
+% [NONE] Unknown command: arg ipv4m 1.2.3.0/9a
+test#
+test# arg ipv6 de4d:b33f::cafe
+cmd2 with 3 args.
+[00] arg@(null): arg
+[01] ipv6@(null): ipv6
+[02] X:X::X:X@foo: de4d:b33f::cafe
+test# arg ipv6 de4d:b3
+ X:X::X:X 02
+test# arg ipv6 de4d:b33f::caf
+ X:X::X:X 02
+test# arg ipv6 de4d:b33f::cafe
+cmd2 with 3 args.
+[00] arg@(null): arg
+[01] ipv6@(null): ipv6
+[02] X:X::X:X@foo: de4d:b33f::cafe
+test# arg ipv6 de4d:b3
+test# arg ipv6 de4d:b33f::caf
+ X:X::X:X 02
+test# arg ipv6 de4d:b33f::cafe
+cmd2 with 3 args.
+[00] arg@(null): arg
+[01] ipv6@(null): ipv6
+[02] X:X::X:X@foo: de4d:b33f::cafe
+test# arg ipv6 de4d:b33f:z::cafe
+% [NONE] Unknown command: arg ipv6 de4d:b33f:z::cafe
+test# arg ipv6 de4d:b33f:cafe:
+% [NONE] Unknown command: arg ipv6 de4d:b33f:cafe:
+test# arg ipv6 ::
+cmd2 with 3 args.
+[00] arg@(null): arg
+[01] ipv6@(null): ipv6
+[02] X:X::X:X@foo: ::
+test# arg ipv6 ::/
+% [NONE] Unknown command: arg ipv6 ::/
+test# arg ipv6 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
+% [NONE] Unknown command: arg ipv6 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
+test# arg ipv6 12::34::56
+% [NONE] Unknown command: arg ipv6 12::34::56
+test# arg ipv6m dead:beef:cafe::/64
+cmd3 with 3 args.
+[00] arg@(null): arg
+[01] ipv6m@(null): ipv6m
+[02] X:X::X:X/M@ipv6m: dead:beef:cafe::/64
+test# arg ipv6m dead:be
+ X:X::X:X/M 02
+test# arg ipv6m dead:beef:cafe:
+ X:X::X:X/M 02
+test# arg ipv6m dead:beef:cafe::/64
+cmd3 with 3 args.
+[00] arg@(null): arg
+[01] ipv6m@(null): ipv6m
+[02] X:X::X:X/M@ipv6m: dead:beef:cafe::/64
+test#
+test# arg range 4
+% [NONE] Unknown command: arg range 4
+test# arg range 5
+cmd4 with 3 args.
+[00] arg@(null): arg
+[01] range@(null): range
+[02] (5-15)@range: 5
+test# arg range 9
+ (5-15) 02
+test# arg range 9
+cmd4 with 3 args.
+[00] arg@(null): arg
+[01] range@(null): range
+[02] (5-15)@range: 9
+test# arg range 15
+cmd4 with 3 args.
+[00] arg@(null): arg
+[01] range@(null): range
+[02] (5-15)@range: 15
+test# arg range 16
+% [NONE] Unknown command: arg range 16
+test# arg range -1
+% [NONE] Unknown command: arg range -1
+test# arg range 99999999999999999999999999999999999999999
+% [NONE] Unknown command: arg range 99999999999999999999999999999999999999999
+test#
+test# arg
+ ipv4 01
+ ipv4m 01
+ ipv6 01
+ ipv6m 01
+ range 01
+test# arg
+% Command incomplete.
+test#
+test# pa
+test# papat
+% Command incomplete.
+test# pat
+a b c d e f
+g h
+test# pat
+% Command incomplete.
+test#
+test# pat a
+% Command incomplete.
+test# pat a a
+cmd5 with 3 args.
+[00] pat@(null): pat
+[01] a@(null): a
+[02] a@(null): a
+test# pat a
+ a 02
+ b 03
+test# pat a b
+cmd5 with 3 args.
+[00] pat@(null): pat
+[01] a@(null): a
+[02] b@(null): b
+test# pat a c
+% There is no matched command.
+test# pat a c
+% [NONE] Unknown command: pat a c
+test# pat a a x
+% [NONE] Unknown command: pat a a x
+test#
+test# pat c a
+% Command incomplete.
+test# pat c a 1.2.3.4
+cmd7 with 4 args.
+[00] pat@(null): pat
+[01] c@(null): c
+[02] a@(null): a
+[03] A.B.C.D@(null): 1.2.3.4
+test# pat c b 2.3.4
+% [NONE] Unknown command: pat c b 2.3.4
+test# pat c c
+ A.B.C.D 05
+test# pat c c x
+% [NONE] Unknown command: pat c c x
+test#
+test# pat d
+% Command incomplete.
+test# pat d
+bar baz foo
+test# pat d
+% Command incomplete.
+test# pat d foo 1.2.3.4
+cmd8 with 4 args.
+[00] pat@(null): pat
+[01] d@(null): d
+[02] foo@(null): foo
+[03] A.B.C.D@foo: 1.2.3.4
+test# pat d foo
+% Command incomplete.
+test# pat d noooo
+% [NONE] Unknown command: pat d noooo
+test# pat d bar 1::2
+cmd8 with 4 args.
+[00] pat@(null): pat
+[01] d@(null): d
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+test# pat d bar 1::2 foo 3.4.5.6
+cmd8 with 6 args.
+[00] pat@(null): pat
+[01] d@(null): d
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+[04] foo@(null): foo
+[05] A.B.C.D@foo: 3.4.5.6
+test# pat d ba
+ bar 04
+ baz 06
+test# pat d baz
+cmd8 with 3 args.
+[00] pat@(null): pat
+[01] d@(null): d
+[02] baz@(null): baz
+test# pat d foo 3.4.5.6 baz
+cmd8 with 5 args.
+[00] pat@(null): pat
+[01] d@(null): d
+[02] foo@(null): foo
+[03] A.B.C.D@foo: 3.4.5.6
+[04] baz@(null): baz
+test#
+test# pat e
+cmd9 with 2 args.
+[00] pat@(null): pat
+[01] e@(null): e
+test# pat e f
+cmd9 with 3 args.
+[00] pat@(null): pat
+[01] e@(null): e
+[02] WORD@e: f
+test# pat e f g
+% [NONE] Unknown command: pat e f g
+test# pat e 1.2.3.4
+cmd9 with 3 args.
+[00] pat@(null): pat
+[01] e@(null): e
+[02] WORD@e: 1.2.3.4
+test#
+test# pat f
+cmd10 with 2 args.
+[00] pat@(null): pat
+[01] f@(null): f
+test# pat f foo
+% [NONE] Unknown command: pat f foo
+test# pat f key
+cmd10 with 3 args.
+[00] pat@(null): pat
+[01] f@(null): f
+[02] key@(null): key
+test#
+test# no pat g
+cmd15 with 3 args.
+[00] no@(null): no
+[01] pat@(null): pat
+[02] g@(null): g
+test# no pat g test
+cmd15 with 4 args.
+[00] no@(null): no
+[01] pat@(null): pat
+[02] g@(null): g
+[03] WORD@g: test
+test# no pat g test more
+% [NONE] Unknown command: no pat g test more
+test#
+test# pat h foo
+ A.B.C.D 04
+test# pat h foo 1.2.3.4 final
+cmd16 with 5 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] foo@(null): foo
+[03] A.B.C.D@foo: 1.2.3.4
+[04] final@(null): final
+test# no pat h foo
+ A.B.C.D 04
+ bar 05
+ final 07
+test# no pat h foo 1.2.3.4 final
+cmd16 with 6 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] foo@(null): foo
+[04] A.B.C.D@foo: 1.2.3.4
+[05] final@(null): final
+test# pat h foo final
+% [NONE] Unknown command: pat h foo final
+test# no pat h foo final
+cmd16 with 5 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] foo@(null): foo
+[04] final@(null): final
+test# pat h bar final
+% [NONE] Unknown command: pat h bar final
+test# no pat h bar final
+% [NONE] Unknown command: no pat h bar final
+test# pat h bar 1::2 final
+cmd16 with 5 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+[04] final@(null): final
+test# no pat h bar 1::2 final
+cmd16 with 6 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] final@(null): final
+test# pat h bar 1::2 foo final
+% [NONE] Unknown command: pat h bar 1::2 foo final
+test# no pat h bar 1::2 foo final
+cmd16 with 7 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] foo@(null): foo
+[06] final@(null): final
+test# pat h bar 1::2 foo 1.2.3.4 final
+cmd16 with 7 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+[04] foo@(null): foo
+[05] A.B.C.D@foo: 1.2.3.4
+[06] final@(null): final
+test# no pat h bar 1::2 foo 1.2.3.4 final
+cmd16 with 8 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] foo@(null): foo
+[06] A.B.C.D@foo: 1.2.3.4
+[07] final@(null): final
+test#
+test# alt a
+test# alt a a
+ WORD 02
+ X:X::X:X 02
+test# alt a ab
+cmd11 with 3 args.
+[00] alt@(null): alt
+[01] a@(null): a
+[02] WORD@a: ab
+test# alt a 1
+test# alt a 1.2
+ A.B.C.D 02
+ WORD 02
+test# alt a 1.2.3.4
+cmd12 with 3 args.
+[00] alt@(null): alt
+[01] a@(null): a
+[02] A.B.C.D@a: 1.2.3.4
+test# alt a 1
+test# alt a 1:2
+ WORD 02
+ X:X::X:X 02
+test# alt a 1:2
+test# alt a 1:2::
+ WORD 02
+ X:X::X:X 02
+test# alt a 1:2::3
+cmd13 with 3 args.
+[00] alt@(null): alt
+[01] a@(null): a
+[02] X:X::X:X@a: 1:2::3
+test#
+test# conf t
+test(config)# do pat d baz
+cmd8 with 3 args.
+[00] pat@(null): pat
+[01] d@(null): d
+[02] baz@(null): baz
+test(config)# exit
+test#
+test# show run
+
+Current configuration:
+!
+frr version @PACKAGE_VERSION@
+frr defaults @DFLT_NAME@
+!
+hostname test
+domainname test.domain
+!
+!
+!
+!
+end
+test# conf t
+test(config)# hostname foohost
+foohost(config)# do show run
+
+Current configuration:
+!
+frr version @PACKAGE_VERSION@
+frr defaults @DFLT_NAME@
+!
+hostname foohost
+domainname test.domain
+!
+!
+!
+!
+end
+foohost(config)#
+end.
diff --git a/tests/lib/cli/test_commands.c b/tests/lib/cli/test_commands.c
new file mode 100644
index 0000000..ea84120
--- /dev/null
+++ b/tests/lib/cli/test_commands.c
@@ -0,0 +1,401 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Test code for lib/command.c
+ *
+ * Copyright (C) 2013 by Open Source Routing.
+ * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This program reads in a list of commandlines from stdin
+ * and calls all the public functions of lib/command.c for
+ * both the given command lines and fuzzed versions thereof.
+ *
+ * The output is currently not validated but only logged. It can
+ * be diffed to find regressions between versions.
+ */
+
+#define REALLY_NEED_PLAIN_GETOPT 1
+
+#include <zebra.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "command.h"
+#include "memory.h"
+#include "vector.h"
+#include "prng.h"
+
+extern vector cmdvec;
+extern struct cmd_node vty_node;
+extern void test_init_cmd(void); /* provided in test-commands-defun.c */
+
+struct event_loop *master; /* dummy for libfrr*/
+
+static vector test_cmds;
+static char test_buf[32768];
+
+static struct cmd_node bgp_node = {
+ .name = "bgp",
+ .node = BGP_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+};
+
+static struct cmd_node rip_node = {
+ .name = "rip",
+ .node = RIP_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+};
+
+static struct cmd_node isis_node = {
+ .name = "isis",
+ .node = ISIS_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+};
+
+static struct cmd_node interface_node = {
+ .name = "interface",
+ .node = INTERFACE_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-if)# ",
+};
+
+static struct cmd_node rmap_node = {
+ .name = "routemap",
+ .node = RMAP_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-route-map)# ",
+};
+
+static struct cmd_node zebra_node = {
+ .name = "zebra",
+ .node = ZEBRA_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+};
+
+static struct cmd_node bgp_vpnv4_node = {
+ .name = "bgp vpnv4",
+ .node = BGP_VPNV4_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+};
+
+static struct cmd_node bgp_ipv4_node = {
+ .name = "bgp ipv4 unicast",
+ .node = BGP_IPV4_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+};
+
+static struct cmd_node bgp_ipv4m_node = {
+ .name = "bgp ipv4 multicast",
+ .node = BGP_IPV4M_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+};
+
+static struct cmd_node bgp_ipv6_node = {
+ .name = "bgp ipv6",
+ .node = BGP_IPV6_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+};
+
+static struct cmd_node bgp_ipv6m_node = {
+ .name = "bgp ipv6 multicast",
+ .node = BGP_IPV6M_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+};
+
+static struct cmd_node ospf_node = {
+ .name = "ospf",
+ .node = OSPF_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+};
+
+static struct cmd_node ripng_node = {
+ .name = "ripng",
+ .node = RIPNG_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+};
+
+static struct cmd_node ospf6_node = {
+ .name = "ospf6",
+ .node = OSPF6_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-ospf6)# ",
+};
+
+static struct cmd_node keychain_node = {
+ .name = "keychain",
+ .node = KEYCHAIN_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-keychain)# ",
+};
+
+static struct cmd_node keychain_key_node = {
+ .name = "keychain key",
+ .node = KEYCHAIN_KEY_NODE,
+ .parent_node = KEYCHAIN_NODE,
+ .prompt = "%s(config-keychain-key)# ",
+};
+
+static int test_callback(const struct cmd_element *cmd, struct vty *vty,
+ int argc, struct cmd_token *argv[])
+{
+ int offset;
+ int rv;
+ int i;
+
+ offset = 0;
+ rv = snprintf(test_buf, sizeof(test_buf), "'%s'", cmd->string);
+ if (rv < 0)
+ abort();
+
+ offset += rv;
+
+ for (i = 0; i < argc; i++) {
+ rv = snprintf(test_buf + offset, sizeof(test_buf) - offset,
+ "%s'%s'", (i == 0) ? ": " : ", ", argv[i]->arg);
+ if (rv < 0)
+ abort();
+ offset += rv;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void test_load(void)
+{
+ char line[4096];
+
+ test_cmds = vector_init(VECTOR_MIN_SIZE);
+
+ while (fgets(line, sizeof(line), stdin) != NULL) {
+ if (strlen(line))
+ line[strlen(line) - 1] = '\0';
+ if (line[0] == '#')
+ continue;
+ vector_set(test_cmds, XSTRDUP(MTYPE_TMP, line));
+ }
+}
+
+static void test_init(void)
+{
+ unsigned int node;
+ unsigned int i;
+ struct cmd_node *cnode;
+ struct cmd_element *cmd;
+
+ cmd_init(1);
+ nb_init(master, NULL, 0, false);
+
+ install_node(&bgp_node);
+ install_node(&rip_node);
+ install_node(&interface_node);
+ install_node(&rmap_node);
+ install_node(&zebra_node);
+ install_node(&bgp_vpnv4_node);
+ install_node(&bgp_ipv4_node);
+ install_node(&bgp_ipv4m_node);
+ install_node(&bgp_ipv6_node);
+ install_node(&bgp_ipv6m_node);
+ install_node(&ospf_node);
+ install_node(&ripng_node);
+ install_node(&ospf6_node);
+ install_node(&keychain_node);
+ install_node(&keychain_key_node);
+ install_node(&isis_node);
+ install_node(&vty_node);
+
+ test_init_cmd();
+
+ for (node = 0; node < vector_active(cmdvec); node++)
+ if ((cnode = vector_slot(cmdvec, node)) != NULL)
+ for (i = 0; i < vector_active(cnode->cmd_vector); i++)
+ if ((cmd = vector_slot(cnode->cmd_vector, i))
+ != NULL) {
+ cmd->daemon = 0;
+ cmd->func = test_callback;
+ }
+ test_load();
+ vty_init_vtysh();
+}
+
+static void test_terminate(void)
+{
+ unsigned int i;
+
+ vty_terminate();
+ for (i = 0; i < vector_active(test_cmds); i++)
+ XFREE(MTYPE_TMP, vector_slot(test_cmds, i));
+ vector_free(test_cmds);
+ cmd_terminate();
+ nb_terminate();
+ yang_terminate();
+}
+
+static void test_run(struct prng *prng, struct vty *vty, const char *cmd,
+ unsigned int edit_dist, unsigned int node_index,
+ int verbose)
+{
+ const char *test_str;
+ vector vline;
+ int ret;
+ unsigned int i;
+ char **completions;
+ unsigned int j;
+ struct cmd_node *cnode;
+ vector descriptions;
+ int appended_null;
+ int no_match;
+
+ test_str = prng_fuzz(
+ prng, cmd,
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_:. /",
+ edit_dist);
+ vline = cmd_make_strvec(test_str);
+
+ if (vline == NULL)
+ return;
+
+ appended_null = 0;
+ for (i = 0; i < vector_active(cmdvec); i++)
+ if ((cnode = vector_slot(cmdvec, i)) != NULL) {
+ if (node_index != (unsigned int)-1 && i != node_index)
+ continue;
+
+ if (appended_null) {
+ vector_unset(vline, vector_active(vline) - 1);
+ appended_null = 0;
+ }
+ vty->node = cnode->node;
+ test_buf[0] = '\0';
+ ret = cmd_execute_command(vline, vty, NULL, 0);
+ no_match = (ret == CMD_ERR_NO_MATCH);
+ if (verbose || !no_match)
+ printf("execute relaxed '%s'@%d: rv==%d%s%s\n",
+ test_str, cnode->node, ret,
+ (test_buf[0] != '\0') ? ", " : "",
+ test_buf);
+
+ vty->node = cnode->node;
+ test_buf[0] = '\0';
+ ret = cmd_execute_command_strict(vline, vty, NULL);
+ if (verbose || !no_match)
+ printf("execute strict '%s'@%d: rv==%d%s%s\n",
+ test_str, cnode->node, ret,
+ (test_buf[0] != '\0') ? ", " : "",
+ test_buf);
+
+ if (isspace((unsigned char)test_str[
+ strlen(test_str) - 1])) {
+ vector_set(vline, NULL);
+ appended_null = 1;
+ }
+
+ vty->node = cnode->node;
+ completions = cmd_complete_command(vline, vty, &ret);
+ if (verbose || !no_match)
+ printf("complete '%s'@%d: rv==%d\n", test_str,
+ cnode->node, ret);
+ if (completions != NULL) {
+ for (j = 0; completions[j] != NULL; j++) {
+ printf(" '%s'\n", completions[j]);
+ XFREE(MTYPE_TMP, completions[j]);
+ }
+ XFREE(MTYPE_TMP, completions);
+ }
+
+ vty->node = cnode->node;
+ descriptions = cmd_describe_command(vline, vty, &ret);
+ if (verbose || !no_match)
+ printf("describe '%s'@%d: rv==%d\n", test_str,
+ cnode->node, ret);
+ if (descriptions != NULL) {
+ for (j = 0; j < vector_active(descriptions);
+ j++) {
+ struct cmd_token *ct =
+ vector_slot(descriptions, j);
+ printf(" '%s' '%s'\n", ct->text,
+ ct->desc);
+ }
+ vector_free(descriptions);
+ }
+ }
+ cmd_free_strvec(vline);
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ struct prng *prng;
+ struct vty *vty;
+ unsigned int edit_distance;
+ unsigned int max_edit_distance;
+ unsigned int node_index;
+ int verbose;
+ unsigned int test_cmd;
+ unsigned int iteration;
+ unsigned int num_iterations;
+
+ max_edit_distance = 3;
+ node_index = -1;
+ verbose = 0;
+
+ while ((opt = getopt(argc, argv, "e:n:v")) != -1) {
+ switch (opt) {
+ case 'e':
+ max_edit_distance = atoi(optarg);
+ break;
+ case 'n':
+ node_index = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ fprintf(stderr,
+ "Usage: %s [-e <edit_dist>] [-n <node_idx>] [-v]\n",
+ argv[0]);
+ exit(1);
+ break;
+ }
+ }
+
+ test_init();
+ prng = prng_new(0);
+
+ vty = vty_new();
+ vty->type = VTY_TERM;
+
+ fprintf(stderr, "Progress:\n0/%u", vector_active(test_cmds));
+ for (test_cmd = 0; test_cmd < vector_active(test_cmds); test_cmd++) {
+ for (edit_distance = 0; edit_distance <= max_edit_distance;
+ edit_distance++) {
+ num_iterations = 1 << edit_distance;
+ num_iterations *= num_iterations * num_iterations;
+
+ for (iteration = 0; iteration < num_iterations;
+ iteration++)
+ test_run(prng, vty,
+ vector_slot(test_cmds, test_cmd),
+ edit_distance, node_index, verbose);
+ }
+ fprintf(stderr, "\r%u/%u", test_cmd + 1,
+ vector_active(test_cmds));
+ }
+ fprintf(stderr, "\nDone.\n");
+
+ vty_close(vty);
+ prng_free(prng);
+ test_terminate();
+ return 0;
+}
diff --git a/tests/lib/cli/test_commands.in b/tests/lib/cli/test_commands.in
new file mode 100644
index 0000000..7fe6279
--- /dev/null
+++ b/tests/lib/cli/test_commands.in
@@ -0,0 +1,216 @@
+#
+#
+# Some randomly chosen valid commands
+#
+#
+area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE
+area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1
+area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1
+area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1
+area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1
+clear bgp 1 out
+clear bgp ipv6 2001:db8::1 out
+clear bgp view VARIABLE * soft
+clear ip bgp 1.2.3.4 ipv4 multicast out
+ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig
+ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1
+network 1.0.0.0/8 area 0
+no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay
+no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay
+no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval
+no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval
+no bgp graceful-restart
+no ipv6 nd mtu 1
+no neighbor 1.2.3.4 distribute-list 1 in
+no neighbor 2001:db8::1 send-community both
+no neighbor VARIABLE maximum-prefix
+redistribute isis route-map VARIABLE metric 0 metric-type 2
+redistribute rip metric 0 route-map VARIABLE metric-type 1
+show bgp community VARIABLE local-AS no-export VARIABLE exact-match
+show bgp ipv6 community no-advertise no-export no-export no-export
+show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match
+show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match
+show bgp view VARIABLE
+show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE
+show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS
+show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise
+show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE
+show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS
+show ip bgp community no-advertise local-AS no-advertise VARIABLE
+show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match
+show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match
+show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match
+show ipv6 bgp community no-export no-export VARIABLE VARIABLE
+show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match
+show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match
+show ipv6 mbgp community local-AS local-AS no-export no-export exact-match
+show ipv6 mbgp community no-export no-export local-AS no-export exact-match
+show ipv6 ospf6 database as-external dump
+show ipv6 ospf6 database inter-prefix 1.2.3.4 detail
+show ipv6 ospf6 database intra-prefix 1.2.3.4 internal
+#
+#
+# Slightly Fuzzed commands
+#
+#
+a8ra 0 range 1.0.0.0/8 adverOise
+accept-lifetime VARIABE 1 VA6IABLE 19I3 VARIABLE 1 VARIABLE 1993
+arAea 1.2.M.4 virtual-link 1.2.3.4 dead-interval 1 dead-interval 1 dead-inter6val 1 transmit-delay 1
+area 0 virtu0al-link 1.2.3.i hello-interval 1 ello-interval 1 transmit-delay 1 retransmit-interval 1
+area 0 virtual-lin 1.2.3.4 retransmit-interval 1 tranwmit-delay 1 retransmit-interval 1 retransmit-interval 1
+area 0 virtual-link 1.2.3.4 retransmit-interal 1 trasmit-dely 1
+area 1.2.3.4 virtual-link 1.2.3.4 deadCinterval 1 dead-intervalK 1 retransmit-interval 1 dead-interval 1
+area 1.2.3.4 virtual-link 1.2.3.4 dead-intervalo I1 dead-interval 1 retransmit-interval1 dead-interval 1
+area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1
+area 1.2.3.4 virtuyl-link 1.2.3.4 dead-interval 1 dead-inervalI 1 retransmit-interval 1 dead-interval 1
+area 1.2.3.4 virual-link 1.2.34 retransmit-interval 1 dead-interval 1 dead-interva 1
+area1.2.83.4 virtual-link 1.2.3.4 retra0smit-interval 1 dead-interval 1 dead-interval 1
+clear bgAp 2001g:dbK::1
+clear ip bgp 1.2.3.4 pv4 mlticat out
+cleau bg i2001:db8::1 rsclient
+de:ug ospf6 messag2 lsreq :recv
+how ip bgp communiQy no-advertise no-adve:tise no-advertise
+ip route 1.0Q0.0/8 1.2.3.s4 reGject
+ipv6 nd prefix 2O01:db8::/32 0 infinEite off-link
+ipv6 nwd prefix 2001:db8::/32 0 infinite oUUff-link
+ipv6 route 2001:db8::/32q2001:db8:k: blackhole 1
+kshow ip rIute bgp
+matcch peer .2.30.4
+mcogin
+mhow ipv6 mbgp community o-advertise yocal-AS no-advertise
+neighbor1.2..4 attribute-unchnged next-hop
+neihbcr 2001:d b8::1 distribute-list 1 in
+nko key-tqring
+no area 0 viertual-link 1.2.3k.4 retransmit-iterval retransmit-interval retransmit-interval hello-interval
+no area 0 virtual-link 1.2.3.4 dead-intaerval dead-intervIl hello-interval retransmit-interval
+no area 0 virtual-link 1.2.3.4 retransmit-interval retransmit-intervIl dead-interval tranImit-deqlay
+no area 0 virtual-link S1.2.3.4 d-ead-interval hello-interval transmit-deay transmit-delay
+no area 1.2.3.4 virtua -link 1.2.3.4 transmit-delay hello-interval hello-interval retransmt-interval
+no area 1.2.3.4 virtual-link 1.2.3.4 dea-iterval retransmit-interva- dead-interval hello-interval
+no area 1.2.3.4 virtual-link 1.2.3.4 hello-interSval dead-interval retransmit-interval transmitdelay
+no a:rea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interSvalW dead-interval retransmit-interval hello-interval
+noarea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval trynsmit-delay hello-interval
+no area 1.2.3.4 virtual-link 1.2.3.4 transmt:delay retransmit-interval retransmit-interval dead-Mnterval
+no ares 1.2.3.4 virtual-link 1.2.3.4 dead-interval retransmit-interval dead-inesval retransmit-interval
+no ayrea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval transmi-delay hello-interval
+no bg2 grace2fuy-restart
+no debug ospk6 nter2face
+noimatch ipv6 addrMss VARIABLE
+nomStch iA next-hop prefix-list
+no neighbCr 200 :db8::1oroute-map VARIABLE export
+no neighbor VARIABLE attributeaw8changed next-hop
+no orea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval ead-interval retransmit-inteSval hello-interval
+no ospcdead-inkerval
+no redistribute kernelrote-map VARIABLE metric 0
+no redistribute s4taik metric 0
+nos Ceighbor 1.2.3.4 route-mapEVARIABLE in
+o :neighbor VAIABLE attribute-unchanged next-hop
+ooa router ip
+redistribute isis meGtric-type2 Q route-map VARIABLE
+redistribute static metric-type 1 metri 0 rowute-map VARIABLE
+set-Koveroadbit
+sh2w ipv6 mbgp comAunity VARIABLE
+shgw bgp ipv6 community no-export VARIABLE no-xport no-expmrt
+shiow Wgp neighbors
+shoAw ip bgpipv4 unicast com6munity no-export no-export no-advertise no-export exact-match
+sho bgp view gARIABLE nyeighbors 2001:db8::1 received-routes
+shoow bgp ommunity local-AS no--export
+show6 bgp community no-advertise local4-AS no-advertise VARIABLE exact-math
+show8 bgp view VARIABLE ipv4 multicast community ARIABLE VARIABLE local-S
+show bgp cCommunity VARIABLE VOARIABL no-advertise
+show bgp cimAunity loal-AS local-AS no-export local-AS
+show bgp cmmunity n-advertise no-export local-S no-advertise
+show bgp communi0y no-export no-Cexport no-0xport no-export
+show bgp communityOlocal-A no-advertise local-WAS
+show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match
+show bgp communiy no-export no-adsvertise VARIABLOE local-AS
+show bgp communiYty no-export VARIABLE VARIABLE locali-AS exact-math
+show bgp commuUityW no-advertis local-AS no-advertise no-advertise
+show bgp commuWnity VAIABLE local-AS no-advertise n-export
+show bgp com:unity no-exportqno-export VARIABLE no-expoIrt exact-match
+show bgp ipv6 community local-AS no-expor no-xport VARIABCLE
+show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE
+show bgp ipv6 community no-advertise no6-export lcal-AS local-AS
+show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math
+show bgp ipv6 comm-unity no-advertise no-export local-AS local-kS exact-match
+show bgp ipv6 community no-export local-AS no-adertise no-adve-tie
+show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE
+show bgp naighbors 201:db8::1 rUeceived-routes
+show bgp viewVAIABLE ipv4 multicast community VARIABLE4no-export no-advertise local-AS
+show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS
+show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export
+show bgp view VARIABLE ipv4 multicast omsunity local-AS VARIABLE no-advertise nUo-export
+show bgp view VARIABLE ipv4 mutiast community no-export no-export VARIBLE no-export
+show bgp view VARIABLE ipv4 unicast 0community VARIABqLE local-AS no-export VARIABwE
+show bgp view VARIABLE ipv4 unicast communeity no-export AcRIABLE no-advertise local-AS
+show bgp view VARIABLE ipv4 unicasU comunity no-export VARIABL no-advertise
+show bgp view VARIABLE ipv6 unicast cocmmunity VARIABLE no-advet6ise VARIABLE
+show bgp view VARIABLE ipvk4 unicast communty no-advertie local-AS local-AS no-export
+show bgp view VARIALE ipv4 multicast cyommunity no-xport local-AS local-AS
+show i6 bge community no-export VARIABLE no-advegtise VARIABLE exact-match
+show iI bgp community no-advertise no-ad2vertsse VARIABLE exact-match
+show ip6osp6 database dump
+show ipA6 bgp community local-AS local-AS no-advertse lo:cal-AS
+show ip bg comunity VARIABLE lcal-AS no-advertise
+show ip bgp communityno-export2no-export no-advertise locaE-AS
+Show ip bgp community no-export loqcal-AS no-adverise no-export
+show ip bgp community no-expor VARIABLEono-export VARIAuBLE
+show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match
+show ip bgp cWmmunity no-expoWrt VARIABLE no-advertise VARIABLEexact-match
+show ip bgp ip4 nicast community no-advertise no-expoIt local-AS local-AS exact-match
+show ip bgp ipAv4 multicast community no-export no-export no-export no-advertiqe exact-mach
+show ip bgp ipv4 Aulticast community no-advertise VARIABLE no-advertisKe no-exort
+show ip bgp ipv4 meuqlticast community VARIABLE VARIABLE no-export n-export
+show ip bgp ipv4 mlticast coQmmunity localg-AS local-AS no-advertise local-AS
+show ip bgp ipv4 multicast communiy VARIABLE no-export VARIABLE no-advertise yxact-atch
+show ip bgp ipv4 unicast commu0nity local-AS no-export no-exrt VARIABLE exact-match
+show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match
+showip bgp ipv4 unicast community no-export VARIABLE no-exp-ort VAR6IABLE exact-match
+show ip bgp ipv4 unicat community no-exportlocal-AS VARIABLE no-export exa0t-match
+show ip bgp ipv4 unicst community no-advertiseG local-AS no-advertise
+show ip bgp i:v4 multicast community VARIABLE VARIABLE VARIABLE no-export eMxact-match
+show ip bgp Mv4 unicast community no-export VARIABLE VARIABLE VAoRIABLE
+show ipgexecommunity-list 1
+show ipkv6 bgp community no-export no-export VARIABL VARIBLE
+show ipv6 bgp commu2nity local-AS local-AS noEadvertise local-AS
+show ipv6 bgp communitK VARIABLE lcocal-AS no-advertie no-advertise exact-match
+show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match
+show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE
+show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match
+show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE
+show ipv6 bgp comu-ity VARIABLE local-AS no-advertise no-export exact-match
+show ipv6 bgp comunity no- export local-AS no-advertisge VARIABLE
+show ipv6 bgp ommunity sno-advcrtise VARIABLE no-export no-advertise exact-match
+show ipv6 igp community no-advertise no-advertise no-ecxpo0rt no-export
+show ipv6 mb communyty VARIABLE
+show ipv6 osp8f6 database nQtwork adv-ruter 1.2.3.4 detail
+show ipv6 ospf6 dataase type-7 adv-router 1.2.3.4 inernal
+show ipv6 ospf6 Edatabase intuer8-prefix 1.2.3.4 detail
+show ipvq6 ospf6 database as-externa detil
+show ip Wbgp ipv4 unicast community no-advertise no-exprt no-export VARIABLEK exact-match
+show ip Ybgp attribute-in ufo
+showMbgp ipv6 community ARIABLE local-AS local-AS no8advertise exact-match
+show p bgp community no-dvertise no-export no-advertiseIno-export exact-match
+show uipv6 mbgp coqmmunKty VARIABLE
+shQw ipv6 mbgp community no-advetise local-AS no-export no-export ex8ct-match
+shuw ipv6 mbgp community VARIABLyUE no-export no-export no-advertise
+shw bgp view VARIABLE ipv4 un0icast Gcommunity no-export VARIABLE no-advertise
+sow ip bgp ipv4 mulicast community no-export no-adertise no-export no-advertise
+sow ipv6 ospf6 databIase as-external adv-router 1.2.3.4
+Whow bgp view VARIAeBLE ipv4 unicast community local-AS no-advrtise no-advertise local-AS
+Wneighbor 1.2.3.4 dot-capabiliy-negotiate
+#
+#
+# Some teststrings explicitly used for keyword commands
+#
+#
+redistribute bgp
+redistribute bgp m 10
+redistribute bgp metric 10 metric-type 1
+redistribute bgp metric 10 metric 10
+redistribute bgp route-map RMAP_REDIST_BGP
+default-information originate metric-type 1 metric 10
+default-information originate always metric-type 1 metric 10
+default-information originate route-map RMAP_DEFAULT
+default-information originate route-map RMAP_DEFAULT metric 10
+default-information originate always metric-type 2 metric 23
diff --git a/tests/lib/cli/test_commands.py b/tests/lib/cli/test_commands.py
new file mode 100644
index 0000000..cf99077
--- /dev/null
+++ b/tests/lib/cli/test_commands.py
@@ -0,0 +1,13 @@
+import frrtest
+import pytest
+import os
+
+
+class TestCommands(frrtest.TestRefOut):
+ program = "./test_commands"
+
+ @pytest.mark.skipif(
+ "QUAGGA_TEST_COMMANDS" not in os.environ, reason="QUAGGA_TEST_COMMANDS not set"
+ )
+ def test_refout(self):
+ return super(TestCommands, self).test_refout()
diff --git a/tests/lib/cli/test_commands.refout b/tests/lib/cli/test_commands.refout
new file mode 100644
index 0000000..2ec3e55
--- /dev/null
+++ b/tests/lib/cli/test_commands.refout
@@ -0,0 +1,1007 @@
+execute relaxed 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (authentication|) (message-digest|null) (message-digest-key|) <1-255> md5 KEY': '0', '1.2.3.4', 'authentication', 'null', 'message-digest-key', '1', 'VARIABLE'
+execute strict 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (authentication|) (message-digest|null) (message-digest-key|) <1-255> md5 KEY': '0', '1.2.3.4', 'authentication', 'null', 'message-digest-key', '1', 'VARIABLE'
+complete 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==2
+describe 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0
+ 'KEY' 'The OSPF password (key)'
+execute relaxed 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'dead-interval', '1', 'hello-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1'
+execute strict 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'dead-interval', '1', 'hello-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1'
+complete 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==2
+describe 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0
+ '<1-65535>' 'Seconds'
+execute relaxed 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'dead-interval', '1', 'retransmit-interval', '1'
+execute strict 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'dead-interval', '1', 'retransmit-interval', '1'
+complete 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==2
+describe 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0
+ '<1-65535>' 'Seconds'
+execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'hello-interval', '1', 'dead-interval', '1'
+execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'hello-interval', '1', 'dead-interval', '1'
+complete 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==2
+describe 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0
+ '<1-65535>' 'Seconds'
+execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1', 'dead-interval', '1'
+execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1', 'dead-interval', '1'
+complete 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==2
+describe 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0
+ '<1-65535>' 'Seconds'
+execute relaxed 'clear bgp 1 out'@4: rv==0, 'clear bgp <1-4294967295> out': '1'
+execute strict 'clear bgp 1 out'@4: rv==0, 'clear bgp <1-4294967295> out': '1'
+complete 'clear bgp 1 out'@4: rv==7
+ 'out'
+describe 'clear bgp 1 out'@4: rv==0
+ 'out' 'Soft reconfig outbound update'
+execute relaxed 'clear bgp ipv6 2001:db8::1 out'@4: rv==0, 'clear bgp ipv6 (A.B.C.D|X:X::X:X) out': '2001:db8::1'
+execute strict 'clear bgp ipv6 2001:db8::1 out'@4: rv==0, 'clear bgp ipv6 (A.B.C.D|X:X::X:X) out': '2001:db8::1'
+complete 'clear bgp ipv6 2001:db8::1 out'@4: rv==7
+ 'out'
+describe 'clear bgp ipv6 2001:db8::1 out'@4: rv==0
+ 'out' 'Soft reconfig outbound update'
+execute relaxed 'clear bgp view VARIABLE * soft'@4: rv==0, 'clear bgp view WORD * soft': 'VARIABLE'
+execute strict 'clear bgp view VARIABLE * soft'@4: rv==0, 'clear bgp view WORD * soft': 'VARIABLE'
+complete 'clear bgp view VARIABLE * soft'@4: rv==7
+ 'soft'
+describe 'clear bgp view VARIABLE * soft'@4: rv==0
+ 'soft' 'Soft reconfig'
+execute relaxed 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0, 'clear ip bgp A.B.C.D ipv4 (unicast|multicast) out': '1.2.3.4', 'multicast'
+execute strict 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0, 'clear ip bgp A.B.C.D ipv4 (unicast|multicast) out': '1.2.3.4', 'multicast'
+complete 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==7
+ 'out'
+describe 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0
+ 'out' 'Soft reconfig outbound update'
+execute relaxed 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0, 'ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) (<0-4294967295>|infinite) (no-autoconfig|)': '2001:db8::/32', 'infinite', 'infinite', 'no-autoconfig'
+execute strict 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0, 'ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) (<0-4294967295>|infinite) (no-autoconfig|)': '2001:db8::/32', 'infinite', 'infinite', 'no-autoconfig'
+complete 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==7
+ 'no-autoconfig'
+describe 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0
+ 'no-autoconfig' 'Do not use prefix for autoconfiguration'
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0
+ '<1-255>' 'Distance value for this prefix'
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2
+execute relaxed 'network 1.0.0.0/8 area 0'@23: rv==0, 'network A.B.C.D/M area (A.B.C.D|<0-4294967295>)': '1.0.0.0/8', '0'
+execute strict 'network 1.0.0.0/8 area 0'@23: rv==0, 'network A.B.C.D/M area (A.B.C.D|<0-4294967295>)': '1.0.0.0/8', '0'
+complete 'network 1.0.0.0/8 area 0'@23: rv==2
+describe 'network 1.0.0.0/8 area 0'@23: rv==0
+ '<0-4294967295>' 'OSPF area ID as a decimal value'
+ 'A.B.C.D' 'OSPF area ID in IP address format'
+execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'hello-interval', 'transmit-delay'
+execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'hello-interval', 'transmit-delay'
+complete 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==7
+ 'transmit-delay'
+describe 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0
+ 'transmit-delay' 'Seconds'
+execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'retransmit-interval', 'transmit-delay'
+execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'retransmit-interval', 'transmit-delay'
+complete 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==7
+ 'transmit-delay'
+describe 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0
+ 'transmit-delay' 'Seconds'
+execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'retransmit-interval', 'dead-interval', 'retransmit-interval', 'hello-interval'
+execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'retransmit-interval', 'dead-interval', 'retransmit-interval', 'hello-interval'
+complete 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==7
+ 'hello-interval'
+describe 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0
+ 'hello-interval' 'Link state transmit delay'
+execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'transmit-delay', 'retransmit-interval', 'retransmit-interval', 'hello-interval'
+execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'transmit-delay', 'retransmit-interval', 'retransmit-interval', 'hello-interval'
+complete 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==7
+ 'hello-interval'
+describe 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0
+ 'hello-interval' 'Link state transmit delay'
+execute relaxed 'no bgp graceful-restart'@17: rv==0, 'no bgp graceful-restart'
+execute strict 'no bgp graceful-restart'@17: rv==0, 'no bgp graceful-restart'
+complete 'no bgp graceful-restart'@17: rv==7
+ 'graceful-restart'
+describe 'no bgp graceful-restart'@17: rv==0
+ 'graceful-restart' 'Graceful restart capability parameters'
+execute relaxed 'no bgp graceful-restart'@18: rv==0, 'no bgp graceful-restart'
+execute strict 'no bgp graceful-restart'@18: rv==2
+complete 'no bgp graceful-restart'@18: rv==2
+describe 'no bgp graceful-restart'@18: rv==2
+execute relaxed 'no bgp graceful-restart'@19: rv==0, 'no bgp graceful-restart'
+execute strict 'no bgp graceful-restart'@19: rv==2
+complete 'no bgp graceful-restart'@19: rv==2
+describe 'no bgp graceful-restart'@19: rv==2
+execute relaxed 'no bgp graceful-restart'@20: rv==0, 'no bgp graceful-restart'
+execute strict 'no bgp graceful-restart'@20: rv==2
+complete 'no bgp graceful-restart'@20: rv==2
+describe 'no bgp graceful-restart'@20: rv==2
+execute relaxed 'no bgp graceful-restart'@21: rv==0, 'no bgp graceful-restart'
+execute strict 'no bgp graceful-restart'@21: rv==2
+complete 'no bgp graceful-restart'@21: rv==2
+describe 'no bgp graceful-restart'@21: rv==2
+execute relaxed 'no bgp graceful-restart'@22: rv==0, 'no bgp graceful-restart'
+execute strict 'no bgp graceful-restart'@22: rv==2
+complete 'no bgp graceful-restart'@22: rv==2
+describe 'no bgp graceful-restart'@22: rv==2
+execute relaxed 'no ipv6 nd mtu 1'@11: rv==0, 'no ipv6 nd mtu <1-65535>': '1'
+execute strict 'no ipv6 nd mtu 1'@11: rv==0, 'no ipv6 nd mtu <1-65535>': '1'
+complete 'no ipv6 nd mtu 1'@11: rv==2
+describe 'no ipv6 nd mtu 1'@11: rv==0
+ '<1-65535>' 'MTU in bytes'
+execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in'
+execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in'
+complete 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==7
+ 'in'
+describe 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0
+ 'in' 'Filter incoming updates'
+execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in'
+execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in'
+complete 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==7
+ 'in'
+describe 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0
+ 'in' 'Filter incoming updates'
+execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in'
+execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in'
+complete 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==7
+ 'in'
+describe 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0
+ 'in' 'Filter incoming updates'
+execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in'
+execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in'
+complete 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==7
+ 'in'
+describe 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0
+ 'in' 'Filter incoming updates'
+execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in'
+execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in'
+complete 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==7
+ 'in'
+describe 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0
+ 'in' 'Filter incoming updates'
+execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in'
+execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in'
+complete 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==7
+ 'in'
+describe 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0
+ 'in' 'Filter incoming updates'
+execute relaxed 'no neighbor 2001:db8::1 send-community both'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+execute strict 'no neighbor 2001:db8::1 send-community both'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+complete 'no neighbor 2001:db8::1 send-community both'@17: rv==7
+ 'both'
+describe 'no neighbor 2001:db8::1 send-community both'@17: rv==0
+ 'both' 'Send Standard and Extended Community attributes'
+execute relaxed 'no neighbor 2001:db8::1 send-community both'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+execute strict 'no neighbor 2001:db8::1 send-community both'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+complete 'no neighbor 2001:db8::1 send-community both'@18: rv==7
+ 'both'
+describe 'no neighbor 2001:db8::1 send-community both'@18: rv==0
+ 'both' 'Send Standard and Extended Community attributes'
+execute relaxed 'no neighbor 2001:db8::1 send-community both'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+execute strict 'no neighbor 2001:db8::1 send-community both'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+complete 'no neighbor 2001:db8::1 send-community both'@19: rv==7
+ 'both'
+describe 'no neighbor 2001:db8::1 send-community both'@19: rv==0
+ 'both' 'Send Standard and Extended Community attributes'
+execute relaxed 'no neighbor 2001:db8::1 send-community both'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+execute strict 'no neighbor 2001:db8::1 send-community both'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+complete 'no neighbor 2001:db8::1 send-community both'@20: rv==7
+ 'both'
+describe 'no neighbor 2001:db8::1 send-community both'@20: rv==0
+ 'both' 'Send Standard and Extended Community attributes'
+execute relaxed 'no neighbor 2001:db8::1 send-community both'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+execute strict 'no neighbor 2001:db8::1 send-community both'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+complete 'no neighbor 2001:db8::1 send-community both'@21: rv==7
+ 'both'
+describe 'no neighbor 2001:db8::1 send-community both'@21: rv==0
+ 'both' 'Send Standard and Extended Community attributes'
+execute relaxed 'no neighbor 2001:db8::1 send-community both'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+execute strict 'no neighbor 2001:db8::1 send-community both'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+complete 'no neighbor 2001:db8::1 send-community both'@22: rv==7
+ 'both'
+describe 'no neighbor 2001:db8::1 send-community both'@22: rv==0
+ 'both' 'Send Standard and Extended Community attributes'
+execute relaxed 'no neighbor VARIABLE maximum-prefix'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+execute strict 'no neighbor VARIABLE maximum-prefix'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+complete 'no neighbor VARIABLE maximum-prefix'@17: rv==7
+ 'maximum-prefix'
+describe 'no neighbor VARIABLE maximum-prefix'@17: rv==0
+ 'maximum-prefix' 'Maximum number of prefix accept from this peer'
+execute relaxed 'no neighbor VARIABLE maximum-prefix'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+execute strict 'no neighbor VARIABLE maximum-prefix'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+complete 'no neighbor VARIABLE maximum-prefix'@18: rv==7
+ 'maximum-prefix'
+describe 'no neighbor VARIABLE maximum-prefix'@18: rv==0
+ 'maximum-prefix' 'Maximum number of prefix accept from this peer'
+execute relaxed 'no neighbor VARIABLE maximum-prefix'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+execute strict 'no neighbor VARIABLE maximum-prefix'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+complete 'no neighbor VARIABLE maximum-prefix'@19: rv==7
+ 'maximum-prefix'
+describe 'no neighbor VARIABLE maximum-prefix'@19: rv==0
+ 'maximum-prefix' 'Maximum number of prefix accept from this peer'
+execute relaxed 'no neighbor VARIABLE maximum-prefix'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+execute strict 'no neighbor VARIABLE maximum-prefix'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+complete 'no neighbor VARIABLE maximum-prefix'@20: rv==7
+ 'maximum-prefix'
+describe 'no neighbor VARIABLE maximum-prefix'@20: rv==0
+ 'maximum-prefix' 'Maximum number of prefix accept from this peer'
+execute relaxed 'no neighbor VARIABLE maximum-prefix'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+execute strict 'no neighbor VARIABLE maximum-prefix'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+complete 'no neighbor VARIABLE maximum-prefix'@21: rv==7
+ 'maximum-prefix'
+describe 'no neighbor VARIABLE maximum-prefix'@21: rv==0
+ 'maximum-prefix' 'Maximum number of prefix accept from this peer'
+execute relaxed 'no neighbor VARIABLE maximum-prefix'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+execute strict 'no neighbor VARIABLE maximum-prefix'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+complete 'no neighbor VARIABLE maximum-prefix'@22: rv==7
+ 'maximum-prefix'
+describe 'no neighbor VARIABLE maximum-prefix'@22: rv==0
+ 'maximum-prefix' 'Maximum number of prefix accept from this peer'
+execute relaxed 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'isis', '0', '2', 'VARIABLE'
+execute strict 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'isis', '0', '2', 'VARIABLE'
+complete 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==7
+ '2'
+describe 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0
+ '2' 'Set OSPF External Type 2 metrics'
+execute relaxed 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'rip', '0', '1', 'VARIABLE'
+execute strict 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'rip', '0', '1', 'VARIABLE'
+complete 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==7
+ '1'
+describe 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0
+ '1' 'Set OSPF External Type 1 metrics'
+execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==7
+ 'exact-match'
+describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==7
+ 'exact-match'
+describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==7
+ 'exact-match'
+describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export'
+execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export'
+complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==7
+ 'no-export'
+describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'no-export' 'Do not export to next AS (well-known community)'
+execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export'
+execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export'
+complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==7
+ 'no-export'
+describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'no-export' 'Do not export to next AS (well-known community)'
+execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export'
+execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export'
+complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==7
+ 'no-export'
+describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'no-export' 'Do not export to next AS (well-known community)'
+execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise'
+execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise'
+complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==7
+ 'exact-match'
+describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise'
+execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise'
+complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==7
+ 'exact-match'
+describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise'
+execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise'
+complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==7
+ 'exact-match'
+describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS'
+execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS'
+complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==7
+ 'exact-match'
+describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS'
+execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS'
+complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==7
+ 'exact-match'
+describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS'
+execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS'
+complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==7
+ 'exact-match'
+describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp view VARIABLE'@1: rv==4
+execute strict 'show bgp view VARIABLE'@1: rv==4
+complete 'show bgp view VARIABLE'@1: rv==2
+describe 'show bgp view VARIABLE'@1: rv==0
+ 'WORD' 'View name'
+execute relaxed 'show bgp view VARIABLE'@2: rv==0, 'show bgp view WORD': 'VARIABLE'
+execute strict 'show bgp view VARIABLE'@2: rv==0, 'show bgp view WORD': 'VARIABLE'
+complete 'show bgp view VARIABLE'@2: rv==2
+describe 'show bgp view VARIABLE'@2: rv==0
+ 'WORD' 'View name'
+execute relaxed 'show bgp view VARIABLE'@4: rv==0, 'show bgp view WORD': 'VARIABLE'
+execute strict 'show bgp view VARIABLE'@4: rv==0, 'show bgp view WORD': 'VARIABLE'
+complete 'show bgp view VARIABLE'@4: rv==2
+describe 'show bgp view VARIABLE'@4: rv==0
+ 'WORD' 'View name'
+execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE'
+execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE'
+complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==2
+describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE'
+execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE'
+complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==2
+describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE'
+execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE'
+complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==2
+describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS'
+complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==7
+ 'local-AS'
+describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS'
+complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==7
+ 'local-AS'
+describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS'
+complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==7
+ 'local-AS'
+describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise'
+execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise'
+complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==7
+ 'no-advertise'
+describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'no-advertise' 'Do not advertise to any peer (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise'
+execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise'
+complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==7
+ 'no-advertise'
+describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'no-advertise' 'Do not advertise to any peer (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise'
+execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise'
+complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==7
+ 'no-advertise'
+describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'no-advertise' 'Do not advertise to any peer (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==2
+describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==2
+describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==2
+describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==7
+ 'local-AS'
+describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==7
+ 'local-AS'
+describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==7
+ 'local-AS'
+describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE'
+execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE'
+complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==2
+describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE'
+execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE'
+complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==2
+describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE'
+execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE'
+complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==2
+describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==7
+ 'exact-match'
+describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==7
+ 'exact-match'
+describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==7
+ 'exact-match'
+describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE'
+execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE'
+complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==7
+ 'exact-match'
+describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE'
+execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE'
+complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==7
+ 'exact-match'
+describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE'
+execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE'
+complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==7
+ 'exact-match'
+describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS'
+execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS'
+complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==7
+ 'exact-match'
+describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS'
+execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS'
+complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==7
+ 'exact-match'
+describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS'
+execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS'
+complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==7
+ 'exact-match'
+describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE'
+execute strict 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE'
+complete 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==2
+describe 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE'
+execute strict 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE'
+complete 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==2
+describe 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise'
+execute strict 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise'
+complete 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==7
+ 'exact-match'
+describe 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise'
+execute strict 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise'
+complete 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==7
+ 'exact-match'
+describe 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE'
+execute strict 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE'
+complete 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==7
+ 'exact-match'
+describe 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE'
+execute strict 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE'
+complete 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==7
+ 'exact-match'
+describe 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export'
+execute strict 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export'
+complete 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==7
+ 'exact-match'
+describe 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export'
+execute strict 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export'
+complete 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==7
+ 'exact-match'
+describe 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export'
+execute strict 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export'
+complete 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==7
+ 'exact-match'
+describe 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export'
+execute strict 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export'
+complete 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==7
+ 'exact-match'
+describe 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 ospf6 database as-external dump'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump'
+execute strict 'show ipv6 ospf6 database as-external dump'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump'
+complete 'show ipv6 ospf6 database as-external dump'@2: rv==7
+ 'dump'
+describe 'show ipv6 ospf6 database as-external dump'@2: rv==0
+ 'dump' 'Dump LSAs'
+execute relaxed 'show ipv6 ospf6 database as-external dump'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump'
+execute strict 'show ipv6 ospf6 database as-external dump'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump'
+complete 'show ipv6 ospf6 database as-external dump'@4: rv==7
+ 'dump'
+describe 'show ipv6 ospf6 database as-external dump'@4: rv==0
+ 'dump' 'Dump LSAs'
+execute relaxed 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail'
+execute strict 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail'
+complete 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==7
+ 'detail'
+describe 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0
+ 'detail' 'Display details of LSAs'
+execute relaxed 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail'
+execute strict 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail'
+complete 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==7
+ 'detail'
+describe 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0
+ 'detail' 'Display details of LSAs'
+execute relaxed 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal'
+execute strict 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal'
+complete 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==7
+ 'internal'
+describe 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0
+ 'internal' 'Display LSA's internal information'
+execute relaxed 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal'
+execute strict 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal'
+complete 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==7
+ 'internal'
+describe 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0
+ 'internal' 'Display LSA's internal information'
+execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'dead-interva', '1', 'retransmit-interval', '1', 'transmit-delay', '1'
+execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==2
+complete 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==2
+describe 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==0
+ '<1-65535>' 'Seconds'
+execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise'
+execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise'
+complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==7
+ 'exact-match'
+describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise'
+execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise'
+complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==7
+ 'exact-match'
+describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise'
+execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise'
+complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==7
+ 'exact-match'
+describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE'
+execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE'
+complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==2
+describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE'
+execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE'
+complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==2
+describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE'
+execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE'
+complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==2
+describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE'
+execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE'
+complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==2
+describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE'
+execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE'
+complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==2
+describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE'
+execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE'
+complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==2
+describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS'
+execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS'
+complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==7
+ 'local-AS'
+describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS'
+execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS'
+complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==7
+ 'local-AS'
+describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS'
+execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS'
+complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==7
+ 'local-AS'
+describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math'
+execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math'
+complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==2
+describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math'
+execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math'
+complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==2
+describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math'
+execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math'
+complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==2
+describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie'
+execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie'
+complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==2
+describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie'
+execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie'
+complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==2
+describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie'
+execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie'
+complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==2
+describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE'
+execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE'
+complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==2
+describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE'
+execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE'
+complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==2
+describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE'
+execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE'
+complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==2
+describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS'
+complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==7
+ 'local-AS'
+describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS'
+complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==7
+ 'local-AS'
+describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS'
+complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==7
+ 'local-AS'
+describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export'
+execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export'
+complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==7
+ 'no-export'
+describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'no-export' 'Do not export to next AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export'
+execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export'
+complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==7
+ 'no-export'
+describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'no-export' 'Do not export to next AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export'
+execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export'
+complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==7
+ 'no-export'
+describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+ 'no-export' 'Do not export to next AS (well-known community)'
+execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE'
+execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE'
+complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==2
+describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE'
+execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE'
+complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==2
+describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE'
+execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE'
+complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==2
+describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match'
+execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match'
+complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==2
+describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match'
+execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match'
+complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==2
+describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match'
+execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match'
+complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==2
+describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export'
+execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export'
+complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==7
+ 'exact-match'
+describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export'
+execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export'
+complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==7
+ 'exact-match'
+describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export'
+execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export'
+complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==7
+ 'exact-match'
+describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise'
+execute strict 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise'
+complete 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==7
+ 'exact-match'
+describe 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise'
+execute strict 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise'
+complete 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==7
+ 'exact-match'
+describe 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE'
+execute strict 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE'
+complete 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==2
+describe 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE'
+execute strict 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE'
+complete 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==2
+describe 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS'
+execute strict 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS'
+complete 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==7
+ 'exact-match'
+describe 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS'
+execute strict 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS'
+complete 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==7
+ 'exact-match'
+describe 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0
+ 'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE'
+execute strict 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE'
+complete 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==2
+describe 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE'
+execute strict 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE'
+complete 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==2
+describe 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0
+ 'AA:NN' 'Community number where AA and NN are <0-65535>'
+execute relaxed 'redistribute bgp'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp)': 'bgp'
+execute strict 'redistribute bgp'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp)': 'bgp'
+complete 'redistribute bgp'@14: rv==7
+ 'bgp'
+describe 'redistribute bgp'@14: rv==0
+ 'bgp' 'Border Gateway Protocol (BGP)'
+execute relaxed 'redistribute bgp'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp)': 'bgp'
+execute strict 'redistribute bgp'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp)': 'bgp'
+complete 'redistribute bgp'@15: rv==7
+ 'bgp'
+describe 'redistribute bgp'@15: rv==0
+ 'bgp' 'Border Gateway Protocol (BGP)'
+execute relaxed 'redistribute bgp'@16: rv==0, 'redistribute (kernel|connected|static|rip|ripng|ospf|ospf6|isis|bgp)': 'bgp'
+execute strict 'redistribute bgp'@16: rv==0, 'redistribute (kernel|connected|static|rip|ripng|ospf|ospf6|isis|bgp)': 'bgp'
+complete 'redistribute bgp'@16: rv==7
+ 'bgp'
+describe 'redistribute bgp'@16: rv==0
+ 'bgp' 'Border Gateway Protocol (BGP)'
+execute relaxed 'redistribute bgp'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', '(null)'
+execute strict 'redistribute bgp'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', '(null)'
+complete 'redistribute bgp'@23: rv==7
+ 'bgp'
+describe 'redistribute bgp'@23: rv==0
+ 'bgp' 'Border Gateway Protocol (BGP)'
+execute relaxed 'redistribute bgp'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp)': 'bgp'
+execute strict 'redistribute bgp'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp)': 'bgp'
+complete 'redistribute bgp'@24: rv==7
+ 'bgp'
+describe 'redistribute bgp'@24: rv==0
+ 'bgp' 'Border Gateway Protocol (BGP)'
+execute relaxed 'redistribute bgp m 10'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp) metric <0-16>': 'bgp', '10'
+execute strict 'redistribute bgp m 10'@14: rv==2
+complete 'redistribute bgp m 10'@14: rv==2
+describe 'redistribute bgp m 10'@14: rv==0
+ '<0-16>' 'Metric value'
+execute relaxed 'redistribute bgp m 10'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp) metric <0-16>': 'bgp', '10'
+execute strict 'redistribute bgp m 10'@15: rv==2
+complete 'redistribute bgp m 10'@15: rv==2
+describe 'redistribute bgp m 10'@15: rv==0
+ '<0-16>' 'Metric value'
+execute relaxed 'redistribute bgp m 10'@23: rv==3
+execute strict 'redistribute bgp m 10'@23: rv==2
+complete 'redistribute bgp m 10'@23: rv==3
+describe 'redistribute bgp m 10'@23: rv==3
+execute relaxed 'redistribute bgp metric 10 metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '10', '1', '(null)'
+execute strict 'redistribute bgp metric 10 metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '10', '1', '(null)'
+complete 'redistribute bgp metric 10 metric-type 1'@23: rv==7
+ '1'
+describe 'redistribute bgp metric 10 metric-type 1'@23: rv==0
+ '1' 'Set OSPF External Type 1 metrics'
+execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp) route-map WORD': 'bgp', 'RMAP_REDIST_BGP'
+execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp) route-map WORD': 'bgp', 'RMAP_REDIST_BGP'
+complete 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==2
+describe 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0
+ 'WORD' 'Pointer to route-map entries'
+execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp) route-map WORD': 'bgp', 'RMAP_REDIST_BGP'
+execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp) route-map WORD': 'bgp', 'RMAP_REDIST_BGP'
+complete 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==2
+describe 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0
+ 'WORD' 'Pointer to route-map entries'
+execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', 'RMAP_REDIST_BGP'
+execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', 'RMAP_REDIST_BGP'
+complete 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==2
+describe 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0
+ 'WORD' 'Pointer to route-map entries'
+execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp) route-map WORD': 'bgp', 'RMAP_REDIST_BGP'
+execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp) route-map WORD': 'bgp', 'RMAP_REDIST_BGP'
+complete 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==2
+describe 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0
+ 'WORD' 'Route map name'
+execute relaxed 'default-information originate metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '1', '(null)'
+execute strict 'default-information originate metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '1', '(null)'
+complete 'default-information originate metric-type 1 metric 10'@23: rv==2
+describe 'default-information originate metric-type 1 metric 10'@23: rv==0
+ '<0-16777214>' 'OSPF metric'
+execute relaxed 'default-information originate always metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '10', '1', '(null)'
+execute strict 'default-information originate always metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '10', '1', '(null)'
+complete 'default-information originate always metric-type 1 metric 10'@23: rv==2
+describe 'default-information originate always metric-type 1 metric 10'@23: rv==0
+ '<0-16777214>' 'OSPF metric'
+execute relaxed 'default-information originate route-map RMAP_DEFAULT'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '(null)', '(null)', 'RMAP_DEFAULT'
+execute strict 'default-information originate route-map RMAP_DEFAULT'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '(null)', '(null)', 'RMAP_DEFAULT'
+complete 'default-information originate route-map RMAP_DEFAULT'@23: rv==2
+describe 'default-information originate route-map RMAP_DEFAULT'@23: rv==0
+ 'WORD' 'Pointer to route-map entries'
+execute relaxed 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '(null)', 'RMAP_DEFAULT'
+execute strict 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '(null)', 'RMAP_DEFAULT'
+complete 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==2
+describe 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0
+ '<0-16777214>' 'OSPF metric'
+execute relaxed 'default-information originate always metric-type 2 metric 23'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '23', '2', '(null)'
+execute strict 'default-information originate always metric-type 2 metric 23'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '23', '2', '(null)'
+complete 'default-information originate always metric-type 2 metric 23'@23: rv==2
+describe 'default-information originate always metric-type 2 metric 23'@23: rv==0
+ '<0-16777214>' 'OSPF metric'
diff --git a/tests/lib/cxxcompat.c b/tests/lib/cxxcompat.c
new file mode 100644
index 0000000..4ad41fc
--- /dev/null
+++ b/tests/lib/cxxcompat.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * C++ compatibility compile-time smoketest
+ * Copyright (C) 2019 David Lamparter for NetDEF, Inc.
+ */
+
+#define test__cplusplus
+
+#include "lib/zebra.h"
+
+#include "lib/agg_table.h"
+#include "lib/bfd.h"
+#include "lib/bitfield.h"
+#include "lib/buffer.h"
+#include "lib/checksum.h"
+#include "lib/command.h"
+#include "lib/command_graph.h"
+#include "lib/command_match.h"
+#include "lib/compiler.h"
+#include "lib/csv.h"
+#include "lib/debug.h"
+#include "lib/distribute.h"
+#include "lib/ferr.h"
+#include "lib/filter.h"
+#include "lib/frr_pthread.h"
+#include "lib/frratomic.h"
+#include "lib/frrstr.h"
+#include "lib/graph.h"
+#include "lib/hash.h"
+#include "lib/hook.h"
+#include "lib/id_alloc.h"
+#include "lib/if.h"
+#include "lib/if_rmap.h"
+#include "lib/imsg.h"
+#include "lib/ipaddr.h"
+#include "lib/jhash.h"
+#include "lib/json.h"
+#include "lib/keychain.h"
+#include "lib/lib_errors.h"
+#include "lib/lib_vty.h"
+#include "lib/libfrr.h"
+#include "lib/libospf.h"
+#include "lib/linklist.h"
+#include "lib/log.h"
+#include "lib/md5.h"
+#include "lib/memory.h"
+#include "lib/mlag.h"
+#include "lib/module.h"
+#include "lib/monotime.h"
+#include "lib/mpls.h"
+#include "lib/network.h"
+#include "lib/nexthop.h"
+#include "lib/nexthop_group.h"
+#include "lib/northbound.h"
+#include "lib/northbound_cli.h"
+#include "lib/northbound_db.h"
+#include "lib/ns.h"
+#include "lib/openbsd-tree.h"
+#include "lib/pbr.h"
+#include "lib/plist.h"
+#include "lib/prefix.h"
+#include "lib/privs.h"
+#include "lib/ptm_lib.h"
+#include "lib/pw.h"
+#include "lib/qobj.h"
+#include "lib/queue.h"
+#include "lib/ringbuf.h"
+#include "lib/routemap.h"
+#include "lib/sbuf.h"
+#include "lib/sha256.h"
+#include "lib/sigevent.h"
+#include "lib/skiplist.h"
+#include "lib/sockopt.h"
+#include "lib/sockunion.h"
+#include "lib/spf_backoff.h"
+#include "lib/srcdest_table.h"
+#include "lib/stream.h"
+#include "lib/table.h"
+#include "lib/termtable.h"
+#include "frrevent.h"
+#include "lib/typesafe.h"
+#include "lib/typerb.h"
+#include "lib/vector.h"
+#include "lib/vlan.h"
+#include "lib/vrf.h"
+#include "lib/vty.h"
+#include "lib/vxlan.h"
+#include "lib/wheel.h"
+/* #include "lib/workqueue.h" -- macro problem with STAILQ_LAST */
+#include "lib/yang.h"
+#include "lib/yang_translator.h"
+#include "lib/yang_wrappers.h"
+#include "lib/zclient.h"
+
+PREDECL_RBTREE_UNIQ(footree);
+struct foo {
+ int dummy;
+ struct footree_item item;
+};
+static int foocmp(const struct foo *a, const struct foo *b)
+{
+ return memcmp(&a->dummy, &b->dummy, sizeof(a->dummy));
+}
+DECLARE_RBTREE_UNIQ(footree, struct foo, item, foocmp);
+
+int main(int argc, char **argv)
+{
+ return 0;
+}
diff --git a/tests/lib/fuzz_zlog.c b/tests/lib/fuzz_zlog.c
new file mode 100644
index 0000000..d308f8e
--- /dev/null
+++ b/tests/lib/fuzz_zlog.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * zlog fuzzer target.
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "zlog_5424.h"
+#include "command.h"
+
+struct input_opts {
+ uint16_t out1_debug;
+ uint16_t out2_debug;
+ uint16_t out3_warn;
+ uint8_t fmt;
+ uint8_t dst;
+};
+
+static char buffer[65536];
+
+int main(int argc, char **argv)
+{
+ struct input_opts io;
+ int fd;
+ int pair[2] = {-1, -1};
+
+ if (read(0, &io, sizeof(io)) != sizeof(io))
+ return 1;
+ if (io.fmt > ZLOG_FMT_LAST)
+ return 1;
+
+ switch (io.dst) {
+ case 0:
+ fd = 1;
+ break;
+ case 1:
+ socketpair(AF_UNIX, SOCK_STREAM, 0, pair);
+ fd = pair[0];
+ break;
+ case 2:
+ socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair);
+ fd = pair[0];
+ break;
+ case 3:
+ socketpair(AF_UNIX, SOCK_DGRAM, 0, pair);
+ fd = pair[0];
+ break;
+ default:
+ return 1;
+ }
+
+ pid_t child = -1;
+
+ if (pair[1] != -1) {
+ child = fork();
+
+ if (child == 0) {
+ char buf[4096];
+
+ close(pair[0]);
+
+ while (read(pair[1], buf, sizeof(buf)) > 0)
+ ;
+ exit(0);
+ } else if (child == -1) {
+ perror("fork");
+ return 1;
+ }
+ close(pair[1]);
+ }
+
+ for (size_t i = 0; i < sizeof(buffer); i++)
+ buffer[i] = (i | 0x20) & 0x7f;
+
+ zlog_aux_init("FUZZBALL: ", LOG_DEBUG);
+ zlog_tls_buffer_init();
+
+ struct zlog_cfg_5424 cfg[1] = {};
+
+ zlog_5424_init(cfg);
+
+ cfg->facility = LOG_DAEMON;
+ cfg->prio_min = LOG_DEBUG;
+ cfg->kw_version = true;
+ cfg->kw_location = true;
+ cfg->kw_uid = true;
+ cfg->kw_ec = true;
+ cfg->kw_args = true;
+
+ cfg->ts_flags = 9;
+ cfg->fmt = io.fmt;
+ cfg->dst = ZLOG_5424_DST_FD;
+ cfg->fd = fd;
+
+ cmd_hostname_set("TEST");
+ cfg->master = event_master_create("TEST");
+
+ zlog_5424_apply_dst(cfg);
+
+ zlog_debug("test #1 %.*s", (int)io.out1_debug, buffer);
+ zlog_debug("test #2 %.*s", (int)io.out2_debug, buffer);
+ zlog_warn("test #1 %.*s", (int)io.out3_warn, buffer);
+
+ zlog_tls_buffer_flush();
+ zlog_tls_buffer_fini();
+
+ /* AFL++ seems to do some weird stuff with its fuzzing target, make
+ * sure the fork() child is zapped here rather than creating hordes
+ * of it.
+ */
+ close(fd);
+ if (child != -1)
+ kill(child, SIGTERM);
+
+ return 0;
+}
diff --git a/tests/lib/fuzz_zlog_inputs.py b/tests/lib/fuzz_zlog_inputs.py
new file mode 100644
index 0000000..ec3f8ae
--- /dev/null
+++ b/tests/lib/fuzz_zlog_inputs.py
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# zlog fuzz-tester input generator
+#
+# Copyright (C) 2021 David Lamparter for NetDEF, Inc.
+
+from itertools import chain
+import struct
+
+lengths = set([128])
+# lengths = [[i, i + 1, i + 3, i - 1, i - 3] for i in lengths]
+# lengths = set([i for i in chain(*lengths) if i >= 0])
+
+dsts = [0, 1, 2, 3]
+fmts = [0, 1, 2, 3]
+
+
+def combo():
+ for l0 in lengths:
+ for l1 in lengths:
+ for l2 in lengths:
+ for fmt in fmts:
+ for dst in dsts:
+ yield (l0, l1, l2, fmt, dst)
+
+
+for i, tup in enumerate(combo()):
+ with open("input/i%d" % i, "wb") as fd:
+ fd.write(struct.pack("HHHBB", *tup))
diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c
new file mode 100644
index 0000000..f82eddd
--- /dev/null
+++ b/tests/lib/northbound/test_oper_data.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#include <zebra.h>
+
+#include "frrevent.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "lib_vty.h"
+#include "log.h"
+#include "northbound.h"
+
+static struct event_loop *master;
+
+struct troute {
+ struct prefix_ipv4 prefix;
+ struct in_addr nexthop;
+ char ifname[IFNAMSIZ];
+ uint8_t metric;
+ bool active;
+};
+
+struct tvrf {
+ char name[32];
+ struct list *interfaces;
+ struct list *routes;
+};
+
+static struct list *vrfs;
+
+/*
+ * XPath: /frr-test-module:frr-test-module/vrfs/vrf
+ */
+static const void *
+frr_test_module_vrfs_vrf_get_next(struct nb_cb_get_next_args *args)
+{
+ struct listnode *node;
+
+ if (args->list_entry == NULL)
+ node = listhead(vrfs);
+ else
+ node = listnextnode((struct listnode *)args->list_entry);
+
+ return node;
+}
+
+static int frr_test_module_vrfs_vrf_get_keys(struct nb_cb_get_keys_args *args)
+{
+ const struct tvrf *vrf;
+
+ vrf = listgetdata((struct listnode *)args->list_entry);
+
+ args->keys->num = 1;
+ strlcpy(args->keys->key[0], vrf->name, sizeof(args->keys->key[0]));
+
+ return NB_OK;
+}
+
+static const void *
+frr_test_module_vrfs_vrf_lookup_entry(struct nb_cb_lookup_entry_args *args)
+{
+ struct listnode *node;
+ struct tvrf *vrf;
+ const char *vrfname;
+
+ vrfname = args->keys->key[0];
+
+ for (ALL_LIST_ELEMENTS_RO(vrfs, node, vrf)) {
+ if (strmatch(vrf->name, vrfname))
+ return node;
+ }
+
+ return NULL;
+}
+
+/*
+ * XPath: /frr-test-module:frr-test-module/vrfs/vrf/name
+ */
+static struct yang_data *
+frr_test_module_vrfs_vrf_name_get_elem(struct nb_cb_get_elem_args *args)
+{
+ const struct tvrf *vrf;
+
+ vrf = listgetdata((struct listnode *)args->list_entry);
+ return yang_data_new_string(args->xpath, vrf->name);
+}
+
+/*
+ * XPath: /frr-test-module:frr-test-module/vrfs/vrf/interfaces/interface
+ */
+static struct yang_data *frr_test_module_vrfs_vrf_interfaces_interface_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const char *interface;
+
+ interface = listgetdata((struct listnode *)args->list_entry);
+ return yang_data_new_string(args->xpath, interface);
+}
+
+static const void *frr_test_module_vrfs_vrf_interfaces_interface_get_next(
+ struct nb_cb_get_next_args *args)
+{
+ const struct tvrf *vrf;
+ struct listnode *node;
+
+ vrf = listgetdata((struct listnode *)args->parent_list_entry);
+ if (args->list_entry == NULL)
+ node = listhead(vrf->interfaces);
+ else
+ node = listnextnode((struct listnode *)args->list_entry);
+
+ return node;
+}
+
+/*
+ * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route
+ */
+static const void *
+frr_test_module_vrfs_vrf_routes_route_get_next(struct nb_cb_get_next_args *args)
+{
+ const struct tvrf *vrf;
+ struct listnode *node;
+
+ vrf = listgetdata((struct listnode *)args->parent_list_entry);
+ if (args->list_entry == NULL)
+ node = listhead(vrf->routes);
+ else
+ node = listnextnode((struct listnode *)args->list_entry);
+
+ return node;
+}
+
+/*
+ * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/prefix
+ */
+static struct yang_data *frr_test_module_vrfs_vrf_routes_route_prefix_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct troute *route;
+
+ route = listgetdata((struct listnode *)args->list_entry);
+ return yang_data_new_ipv4p(args->xpath, &route->prefix);
+}
+
+/*
+ * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/next-hop
+ */
+static struct yang_data *
+frr_test_module_vrfs_vrf_routes_route_next_hop_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct troute *route;
+
+ route = listgetdata((struct listnode *)args->list_entry);
+ return yang_data_new_ipv4(args->xpath, &route->nexthop);
+}
+
+/*
+ * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/interface
+ */
+static struct yang_data *
+frr_test_module_vrfs_vrf_routes_route_interface_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct troute *route;
+
+ route = listgetdata((struct listnode *)args->list_entry);
+ return yang_data_new_string(args->xpath, route->ifname);
+}
+
+/*
+ * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/metric
+ */
+static struct yang_data *frr_test_module_vrfs_vrf_routes_route_metric_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct troute *route;
+
+ route = listgetdata((struct listnode *)args->list_entry);
+ return yang_data_new_uint8(args->xpath, route->metric);
+}
+
+/*
+ * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/active
+ */
+static struct yang_data *frr_test_module_vrfs_vrf_routes_route_active_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ const struct troute *route;
+
+ route = listgetdata((struct listnode *)args->list_entry);
+ if (route->active)
+ return yang_data_new(args->xpath, NULL);
+
+ return NULL;
+}
+
+/* clang-format off */
+const struct frr_yang_module_info frr_test_module_info = {
+ .name = "frr-test-module",
+ .nodes = {
+ {
+ .xpath = "/frr-test-module:frr-test-module/vrfs/vrf",
+ .cbs.get_next = frr_test_module_vrfs_vrf_get_next,
+ .cbs.get_keys = frr_test_module_vrfs_vrf_get_keys,
+ .cbs.lookup_entry = frr_test_module_vrfs_vrf_lookup_entry,
+ },
+ {
+ .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/name",
+ .cbs.get_elem = frr_test_module_vrfs_vrf_name_get_elem,
+ },
+ {
+ .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/interfaces/interface",
+ .cbs.get_elem = frr_test_module_vrfs_vrf_interfaces_interface_get_elem,
+ .cbs.get_next = frr_test_module_vrfs_vrf_interfaces_interface_get_next,
+ },
+ {
+ .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route",
+ .cbs.get_next = frr_test_module_vrfs_vrf_routes_route_get_next,
+ },
+ {
+ .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/prefix",
+ .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_prefix_get_elem,
+ },
+ {
+ .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/next-hop",
+ .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_next_hop_get_elem,
+ },
+ {
+ .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/interface",
+ .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_interface_get_elem,
+ },
+ {
+ .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/metric",
+ .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_metric_get_elem,
+ },
+ {
+ .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/active",
+ .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_active_get_elem,
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
+/* clang-format on */
+
+static const struct frr_yang_module_info *const modules[] = {
+ &frr_test_module_info,
+};
+
+static void create_data(unsigned int num_vrfs, unsigned int num_interfaces,
+ unsigned int num_routes)
+{
+ struct prefix_ipv4 base_prefix;
+ struct in_addr base_nexthop;
+
+ (void)str2prefix_ipv4("10.0.0.0/32", &base_prefix);
+ (void)inet_pton(AF_INET, "172.16.0.0", &base_nexthop);
+
+ vrfs = list_new();
+
+ /* Create VRFs. */
+ for (unsigned int i = 0; i < num_vrfs; i++) {
+ struct tvrf *vrf;
+
+ vrf = XCALLOC(MTYPE_TMP, sizeof(*vrf));
+ snprintf(vrf->name, sizeof(vrf->name), "vrf%u", i);
+ vrf->interfaces = list_new();
+ vrf->routes = list_new();
+
+ /* Create interfaces. */
+ for (unsigned int j = 0; j < num_interfaces; j++) {
+ char ifname[32];
+ char *interface;
+
+ snprintf(ifname, sizeof(ifname), "eth%u", j);
+ interface = XSTRDUP(MTYPE_TMP, ifname);
+ listnode_add(vrf->interfaces, interface);
+ }
+
+ /* Create routes. */
+ for (unsigned int j = 0; j < num_routes; j++) {
+ struct troute *route;
+
+ route = XCALLOC(MTYPE_TMP, sizeof(*route));
+
+ memcpy(&route->prefix, &base_prefix,
+ sizeof(route->prefix));
+ route->prefix.prefix.s_addr =
+ htonl(ntohl(route->prefix.prefix.s_addr) + j);
+
+ memcpy(&route->nexthop, &base_nexthop,
+ sizeof(route->nexthop));
+ route->nexthop.s_addr =
+ htonl(ntohl(route->nexthop.s_addr) + j);
+
+ snprintf(route->ifname, sizeof(route->ifname), "eth%u",
+ j);
+ route->metric = j % 256;
+ route->active = (j % 2 == 0);
+ listnode_add(vrf->routes, route);
+ }
+
+ listnode_add(vrfs, vrf);
+ }
+}
+
+static void interface_delete(void *ptr)
+{
+ char *interface = ptr;
+
+ XFREE(MTYPE_TMP, interface);
+}
+
+static void route_delete(void *ptr)
+{
+ struct troute *route = ptr;
+
+ XFREE(MTYPE_TMP, route);
+}
+
+static void vrf_delete(void *ptr)
+{
+ struct tvrf *vrf = ptr;
+
+ vrf->interfaces->del = interface_delete;
+ list_delete(&vrf->interfaces);
+ vrf->routes->del = route_delete;
+ list_delete(&vrf->routes);
+ XFREE(MTYPE_TMP, vrf);
+}
+
+static void delete_data(void)
+{
+ vrfs->del = vrf_delete;
+ list_delete(&vrfs);
+}
+
+static void vty_do_exit(int isexit)
+{
+ printf("\nend.\n");
+
+ delete_data();
+
+ cmd_terminate();
+ vty_terminate();
+ nb_terminate();
+ yang_terminate();
+ event_master_free(master);
+
+ log_memstats(stderr, "test-nb-oper-data");
+ if (!isexit)
+ exit(0);
+}
+
+/* main routine. */
+int main(int argc, char **argv)
+{
+ struct event thread;
+ unsigned int num_vrfs = 2;
+ unsigned int num_interfaces = 4;
+ unsigned int num_routes = 6;
+
+ if (argc > 1)
+ num_vrfs = atoi(argv[1]);
+ if (argc > 2)
+ num_interfaces = atoi(argv[2]);
+ if (argc > 3)
+ num_routes = atoi(argv[3]);
+
+ /* Set umask before anything for security */
+ umask(0027);
+
+ /* master init. */
+ master = event_master_create(NULL);
+
+ zlog_aux_init("NONE: ", ZLOG_DISABLED);
+
+ /* Library inits. */
+ cmd_init(1);
+ cmd_hostname_set("test");
+ vty_init(master, false);
+ lib_cmd_init();
+ nb_init(master, modules, array_size(modules), false);
+
+ /* Create artificial data. */
+ create_data(num_vrfs, num_interfaces, num_routes);
+
+ /* Read input from .in file. */
+ vty_stdio(vty_do_exit);
+
+ /* Fetch next active thread. */
+ while (event_fetch(master, &thread))
+ event_call(&thread);
+
+ /* Not reached. */
+ exit(0);
+}
diff --git a/tests/lib/northbound/test_oper_data.in b/tests/lib/northbound/test_oper_data.in
new file mode 100644
index 0000000..a6c4f87
--- /dev/null
+++ b/tests/lib/northbound/test_oper_data.in
@@ -0,0 +1 @@
+show yang operational-data /frr-test-module:frr-test-module
diff --git a/tests/lib/northbound/test_oper_data.py b/tests/lib/northbound/test_oper_data.py
new file mode 100644
index 0000000..a02bf05
--- /dev/null
+++ b/tests/lib/northbound/test_oper_data.py
@@ -0,0 +1,5 @@
+import frrtest
+
+
+class TestNbOperData(frrtest.TestRefOut):
+ program = "./test_oper_data"
diff --git a/tests/lib/northbound/test_oper_data.refout b/tests/lib/northbound/test_oper_data.refout
new file mode 100644
index 0000000..57ecd2f
--- /dev/null
+++ b/tests/lib/northbound/test_oper_data.refout
@@ -0,0 +1,119 @@
+test# show yang operational-data /frr-test-module:frr-test-module
+{
+ "frr-test-module:frr-test-module": {
+ "vrfs": {
+ "vrf": [
+ {
+ "name": "vrf0",
+ "interfaces": {
+ "interface": [
+ "eth0",
+ "eth1",
+ "eth2",
+ "eth3"
+ ]
+ },
+ "routes": {
+ "route": [
+ {
+ "prefix": "10.0.0.0/32",
+ "next-hop": "172.16.0.0",
+ "interface": "eth0",
+ "metric": 0,
+ "active": [null]
+ },
+ {
+ "prefix": "10.0.0.1/32",
+ "next-hop": "172.16.0.1",
+ "interface": "eth1",
+ "metric": 1
+ },
+ {
+ "prefix": "10.0.0.2/32",
+ "next-hop": "172.16.0.2",
+ "interface": "eth2",
+ "metric": 2,
+ "active": [null]
+ },
+ {
+ "prefix": "10.0.0.3/32",
+ "next-hop": "172.16.0.3",
+ "interface": "eth3",
+ "metric": 3
+ },
+ {
+ "prefix": "10.0.0.4/32",
+ "next-hop": "172.16.0.4",
+ "interface": "eth4",
+ "metric": 4,
+ "active": [null]
+ },
+ {
+ "prefix": "10.0.0.5/32",
+ "next-hop": "172.16.0.5",
+ "interface": "eth5",
+ "metric": 5
+ }
+ ]
+ }
+ },
+ {
+ "name": "vrf1",
+ "interfaces": {
+ "interface": [
+ "eth0",
+ "eth1",
+ "eth2",
+ "eth3"
+ ]
+ },
+ "routes": {
+ "route": [
+ {
+ "prefix": "10.0.0.0/32",
+ "next-hop": "172.16.0.0",
+ "interface": "eth0",
+ "metric": 0,
+ "active": [null]
+ },
+ {
+ "prefix": "10.0.0.1/32",
+ "next-hop": "172.16.0.1",
+ "interface": "eth1",
+ "metric": 1
+ },
+ {
+ "prefix": "10.0.0.2/32",
+ "next-hop": "172.16.0.2",
+ "interface": "eth2",
+ "metric": 2,
+ "active": [null]
+ },
+ {
+ "prefix": "10.0.0.3/32",
+ "next-hop": "172.16.0.3",
+ "interface": "eth3",
+ "metric": 3
+ },
+ {
+ "prefix": "10.0.0.4/32",
+ "next-hop": "172.16.0.4",
+ "interface": "eth4",
+ "metric": 4,
+ "active": [null]
+ },
+ {
+ "prefix": "10.0.0.5/32",
+ "next-hop": "172.16.0.5",
+ "interface": "eth5",
+ "metric": 5
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+}
+test#
+end.
diff --git a/tests/lib/script1.lua b/tests/lib/script1.lua
new file mode 100644
index 0000000..6361c96
--- /dev/null
+++ b/tests/lib/script1.lua
@@ -0,0 +1,54 @@
+
+-- Positive testing
+
+function foo(a, b)
+ a = a + 1
+ b = b + 1
+ return {
+ a = a,
+ b = b,
+ }
+end
+
+function bar(a, b)
+ a = a + 1
+ b = b + 1
+ c = 303
+ return {
+ b = b,
+ c = c,
+ }
+end
+
+function fact(n)
+ -- outer function must return a table
+ -- inner functions can be used to recurse or as helpers
+ function helper(m)
+ if m == 0 then
+ return 1
+ else
+ return m * helper(m - 1)
+ end
+ end
+ return {
+ ans = helper(n)
+ }
+end
+
+-- Negative testing
+
+function bad_return1()
+end
+
+function bad_return2()
+ return 123
+end
+
+function bad_return3()
+ return {}
+end
+
+function bad_return4()
+ error("Something bad!")
+end
+
diff --git a/tests/lib/subdir.am b/tests/lib/subdir.am
new file mode 100644
index 0000000..6c1be50
--- /dev/null
+++ b/tests/lib/subdir.am
@@ -0,0 +1,385 @@
+##############################################################################
+if SCRIPTING
+check_PROGRAMS += tests/lib/test_frrlua
+endif
+tests_lib_test_frrlua_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_frrlua_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_frrlua_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_frrlua_SOURCES = tests/lib/test_frrlua.c
+EXTRA_DIST += tests/lib/test_frrlua.py
+
+if SCRIPTING
+check_PROGRAMS += tests/lib/test_frrscript
+endif
+tests_lib_test_frrscript_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_frrscript_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_frrscript_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_frrscript_SOURCES = tests/lib/test_frrscript.c
+EXTRA_tests_lib_test_frrscript_DEPENDENCIES = copy_script
+EXTRA_DIST += tests/lib/test_frrscript.py tests/lib/script1.lua
+
+# For out-of-tree build, lua script needs to be in the build dir, rather than
+# just available somewhere in the VPATH
+copy_script: tests/lib/script1.lua
+ test -e tests/lib/script1.lua || \
+ $(INSTALL_SCRIPT) $< tests/lib/script1.lua
+
+##############################################################################
+GRPC_TESTS_LDADD = staticd/libstatic.a grpc/libfrrgrpc_pb.la -lgrpc++ -lprotobuf $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm
+
+if GRPC
+check_PROGRAMS += tests/lib/test_grpc
+endif
+tests_lib_test_grpc_CXXFLAGS = $(WERROR) $(TESTS_CXXFLAGS)
+tests_lib_test_grpc_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_grpc_LDADD = $(GRPC_TESTS_LDADD)
+tests_lib_test_grpc_SOURCES = tests/lib/test_grpc.cpp
+
+
+##############################################################################
+if ZEROMQ
+check_PROGRAMS += tests/lib/test_zmq
+endif
+tests_lib_test_zmq_CFLAGS = $(TESTS_CFLAGS) $(ZEROMQ_CFLAGS)
+tests_lib_test_zmq_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_zmq_LDADD = lib/libfrrzmq.la $(ALL_TESTS_LDADD) $(ZEROMQ_LIBS)
+tests_lib_test_zmq_SOURCES = tests/lib/test_zmq.c
+
+
+##############################################################################
+if CARES
+check_PROGRAMS += tests/lib/test_resolver
+endif
+tests_lib_test_resolver_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_resolver_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_resolver_LDADD = $(ALL_TESTS_LDADD) lib/libfrrcares.la
+tests_lib_test_resolver_SOURCES = tests/lib/test_resolver.c tests/lib/cli/common_cli.c
+
+
+##############################################################################
+noinst_HEADERS += \
+ tests/helpers/c/prng.h \
+ tests/helpers/c/tests.h \
+ tests/lib/cli/common_cli.h \
+ # end
+
+
+check_PROGRAMS += tests/lib/cxxcompat
+tests_lib_cxxcompat_CFLAGS = $(TESTS_CFLAGS) $(CXX_COMPAT_CFLAGS) $(WERROR)
+tests_lib_cxxcompat_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_cxxcompat_SOURCES = tests/lib/cxxcompat.c
+tests_lib_cxxcompat_LDADD = $(ALL_TESTS_LDADD)
+
+
+check_PROGRAMS += tests/lib/fuzz_zlog
+tests_lib_fuzz_zlog_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_fuzz_zlog_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_fuzz_zlog_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_fuzz_zlog_SOURCES = tests/lib/fuzz_zlog.c
+EXTRA_DIST += tests/lib/fuzz_zlog_inputs.py
+
+
+check_PROGRAMS += tests/lib/cli/test_cli
+tests_lib_cli_test_cli_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_cli_test_cli_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_cli_test_cli_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_cli_test_cli_SOURCES = tests/lib/cli/test_cli.c tests/lib/cli/common_cli.c
+clippy_scan += tests/lib/cli/test_cli.c
+EXTRA_DIST += \
+ tests/lib/cli/test_cli.in \
+ tests/lib/cli/test_cli.py \
+ tests/lib/cli/test_cli.refout \
+ # end
+
+
+check_PROGRAMS += tests/lib/cli/test_commands
+tests_lib_cli_test_commands_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_cli_test_commands_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_cli_test_commands_LDADD = $(ALL_TESTS_LDADD)
+nodist_tests_lib_cli_test_commands_SOURCES = tests/lib/cli/test_commands_defun.c
+tests_lib_cli_test_commands_SOURCES = tests/lib/cli/test_commands.c tests/helpers/c/prng.c
+tests/lib/cli/test_commands_defun.c: vtysh/vtysh_cmd.c
+ @$(MKDIR_P) tests/lib/cli
+ $(AM_V_GEN)sed \
+ -e 's%"vtysh/vtysh\.h"%"tests/helpers/c/tests.h"%' \
+ -e 's/vtysh_init_cmd/test_init_cmd/' \
+ -e 's/VTYSH_[A-Z][A-Z_0-9]*/0/g' \
+ < vtysh/vtysh_cmd.c \
+ > "$@"
+CLEANFILES += tests/lib/cli/test_commands_defun.c
+EXTRA_DIST += \
+ tests/lib/cli/test_commands.in \
+ tests/lib/cli/test_commands.py \
+ tests/lib/cli/test_commands.refout \
+ # end
+
+
+check_PROGRAMS += tests/lib/northbound/test_oper_data
+tests_lib_northbound_test_oper_data_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_northbound_test_oper_data_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_northbound_test_oper_data_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_northbound_test_oper_data_SOURCES = tests/lib/northbound/test_oper_data.c
+nodist_tests_lib_northbound_test_oper_data_SOURCES = yang/frr-test-module.yang.c
+EXTRA_DIST += \
+ tests/lib/northbound/test_oper_data.in \
+ tests/lib/northbound/test_oper_data.py \
+ tests/lib/northbound/test_oper_data.refout \
+ # end
+
+
+check_PROGRAMS += tests/lib/test_assert
+tests_lib_test_assert_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_assert_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_assert_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_assert_SOURCES = tests/lib/test_assert.c
+EXTRA_DIST += tests/lib/test_assert.py
+
+
+check_PROGRAMS += tests/lib/test_atomlist
+tests_lib_test_atomlist_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_atomlist_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_atomlist_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_atomlist_SOURCES = tests/lib/test_atomlist.c
+EXTRA_DIST += tests/lib/test_atomlist.py
+
+
+check_PROGRAMS += tests/lib/test_buffer
+tests_lib_test_buffer_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_buffer_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_buffer_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_buffer_SOURCES = tests/lib/test_buffer.c
+
+
+check_PROGRAMS += tests/lib/test_checksum
+tests_lib_test_checksum_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_checksum_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_checksum_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_checksum_SOURCES = tests/lib/test_checksum.c tests/helpers/c/prng.c
+
+
+check_PROGRAMS += tests/lib/test_darr
+tests_lib_test_darr_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_darr_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_darr_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_darr_SOURCES = tests/lib/test_darr.c
+
+
+check_PROGRAMS += tests/lib/test_graph
+tests_lib_test_graph_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_graph_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_graph_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_graph_SOURCES = tests/lib/test_graph.c
+EXTRA_DIST += \
+ tests/lib/test_graph.py \
+ tests/lib/test_graph.refout \
+ # end
+
+
+check_PROGRAMS += tests/lib/test_heavy
+tests_lib_test_heavy_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_heavy_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_heavy_LDADD = $(ALL_TESTS_LDADD) -lm
+tests_lib_test_heavy_SOURCES = tests/lib/test_heavy.c tests/helpers/c/main.c
+
+
+check_PROGRAMS += tests/lib/test_heavy_thread
+tests_lib_test_heavy_thread_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_heavy_thread_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_heavy_thread_LDADD = $(ALL_TESTS_LDADD) -lm
+tests_lib_test_heavy_thread_SOURCES = tests/lib/test_heavy_thread.c tests/helpers/c/main.c
+
+
+check_PROGRAMS += tests/lib/test_heavy_wq
+tests_lib_test_heavy_wq_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_heavy_wq_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_heavy_wq_LDADD = $(ALL_TESTS_LDADD) -lm
+tests_lib_test_heavy_wq_SOURCES = tests/lib/test_heavy_wq.c tests/helpers/c/main.c
+
+
+check_PROGRAMS += tests/lib/test_idalloc
+tests_lib_test_idalloc_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_idalloc_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_idalloc_SOURCES = tests/lib/test_idalloc.c
+
+
+check_PROGRAMS += tests/lib/test_memory
+tests_lib_test_memory_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_memory_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_memory_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_memory_SOURCES = tests/lib/test_memory.c
+
+
+check_PROGRAMS += tests/lib/test_nexthop_iter
+tests_lib_test_nexthop_iter_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_nexthop_iter_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_nexthop_iter_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_nexthop_iter_SOURCES = tests/lib/test_nexthop_iter.c tests/helpers/c/prng.c
+EXTRA_DIST += tests/lib/test_nexthop_iter.py
+
+
+check_PROGRAMS += tests/lib/test_nexthop
+tests_lib_test_nexthop_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_nexthop_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_nexthop_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_nexthop_SOURCES = tests/lib/test_nexthop.c
+EXTRA_DIST += tests/lib/test_nexthop.py
+
+
+check_PROGRAMS += tests/lib/test_ntop
+tests_lib_test_ntop_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_ntop_CPPFLAGS = $(CPPFLAGS_BASE) # no assert override
+tests_lib_test_ntop_LDADD = # none
+tests_lib_test_ntop_SOURCES = tests/lib/test_ntop.c tests/helpers/c/prng.c
+EXTRA_DIST += tests/lib/test_ntop.py
+
+
+check_PROGRAMS += tests/lib/test_plist
+tests_lib_test_plist_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_plist_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_plist_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_plist_SOURCES = tests/lib/test_plist.c tests/lib/cli/common_cli.c
+
+
+check_PROGRAMS += tests/lib/test_prefix2str
+tests_lib_test_prefix2str_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_prefix2str_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_prefix2str_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_prefix2str_SOURCES = tests/lib/test_prefix2str.c tests/helpers/c/prng.c
+EXTRA_DIST += tests/lib/test_prefix2str.py
+
+
+check_PROGRAMS += tests/lib/test_printfrr
+tests_lib_test_printfrr_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_printfrr_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_printfrr_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_printfrr_SOURCES = tests/lib/test_printfrr.c
+EXTRA_DIST += tests/lib/test_printfrr.py
+
+
+check_PROGRAMS += tests/lib/test_privs
+tests_lib_test_privs_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_privs_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_privs_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_privs_SOURCES = tests/lib/test_privs.c
+
+
+check_PROGRAMS += tests/lib/test_ringbuf
+tests_lib_test_ringbuf_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_ringbuf_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_ringbuf_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_ringbuf_SOURCES = tests/lib/test_ringbuf.c
+EXTRA_DIST += tests/lib/test_ringbuf.py
+
+
+check_PROGRAMS += tests/lib/test_segv
+tests_lib_test_segv_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_segv_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_segv_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_segv_SOURCES = tests/lib/test_segv.c
+
+
+check_PROGRAMS += tests/lib/test_seqlock
+tests_lib_test_seqlock_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_seqlock_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_seqlock_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_seqlock_SOURCES = tests/lib/test_seqlock.c
+
+
+check_PROGRAMS += tests/lib/test_sig
+tests_lib_test_sig_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_sig_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_sig_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_sig_SOURCES = tests/lib/test_sig.c
+
+
+check_PROGRAMS += tests/lib/test_skiplist
+tests_lib_test_skiplist_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_skiplist_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_skiplist_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_skiplist_SOURCES = tests/lib/test_skiplist.c
+
+
+check_PROGRAMS += tests/lib/test_srcdest_table
+tests_lib_test_srcdest_table_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_srcdest_table_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_srcdest_table_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_srcdest_table_SOURCES = tests/lib/test_srcdest_table.c tests/helpers/c/prng.c
+EXTRA_DIST += tests/lib/test_srcdest_table.py
+
+
+check_PROGRAMS += tests/lib/test_stream
+tests_lib_test_stream_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_stream_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_stream_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_stream_SOURCES = tests/lib/test_stream.c
+EXTRA_DIST += \
+ tests/lib/test_stream.py \
+ tests/lib/test_stream.refout \
+ # end
+
+
+check_PROGRAMS += tests/lib/test_table
+tests_lib_test_table_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_table_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_table_LDADD = $(ALL_TESTS_LDADD) -lm
+tests_lib_test_table_SOURCES = tests/lib/test_table.c
+EXTRA_DIST += tests/lib/test_table.py
+
+
+check_PROGRAMS += tests/lib/test_timer_correctness
+tests_lib_test_timer_correctness_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_timer_correctness_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_timer_correctness_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_timer_correctness_SOURCES = tests/lib/test_timer_correctness.c tests/helpers/c/prng.c
+EXTRA_DIST += tests/lib/test_timer_correctness.py
+
+
+check_PROGRAMS += tests/lib/test_timer_performance
+tests_lib_test_timer_performance_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_timer_performance_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_timer_performance_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_timer_performance_SOURCES = tests/lib/test_timer_performance.c tests/helpers/c/prng.c
+
+
+check_PROGRAMS += tests/lib/test_ttable
+tests_lib_test_ttable_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_ttable_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_ttable_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_ttable_SOURCES = tests/lib/test_ttable.c
+EXTRA_DIST += \
+ tests/lib/test_ttable.py \
+ tests/lib/test_ttable.refout \
+ # end
+
+
+check_PROGRAMS += tests/lib/test_typelist
+tests_lib_test_typelist_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_typelist_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_typelist_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_typelist_SOURCES = tests/lib/test_typelist.c tests/helpers/c/prng.c
+noinst_HEADERS += tests/lib/test_typelist.h
+EXTRA_DIST += tests/lib/test_typelist.py
+
+
+check_PROGRAMS += tests/lib/test_versioncmp
+tests_lib_test_versioncmp_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_versioncmp_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_versioncmp_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_versioncmp_SOURCES = tests/lib/test_versioncmp.c
+EXTRA_DIST += tests/lib/test_versioncmp.py
+
+
+check_PROGRAMS += tests/lib/test_xref
+tests_lib_test_xref_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_xref_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_xref_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_xref_SOURCES = tests/lib/test_xref.c
+EXTRA_DIST += tests/lib/test_xref.py
+
+
+check_PROGRAMS += tests/lib/test_zlog
+tests_lib_test_zlog_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_zlog_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_zlog_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_zlog_SOURCES = tests/lib/test_zlog.c
+EXTRA_DIST += tests/lib/test_zlog.py
diff --git a/tests/lib/test_assert.c b/tests/lib/test_assert.c
new file mode 100644
index 0000000..4440075
--- /dev/null
+++ b/tests/lib/test_assert.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Quick test for assert()
+ * Copyright (C) 2021 David Lamparter for NetDEF, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* make sure this works with assert.h & nothing else. also check the include
+ * shadowing, we don't want to pick up system assert.h
+ */
+#include <assert.h>
+
+__attribute__((noinline))
+static void func_for_bt(int number)
+{
+ assert(number > 2);
+ assertf(number > 3, "(A) the number was %d", number);
+}
+
+#include <zebra.h>
+#include "lib/zlog.h"
+#include "frrevent.h"
+#include "lib/sigevent.h"
+
+int main(int argc, char **argv)
+{
+ int number = 10;
+ struct event_loop *master;
+
+ zlog_aux_init("NONE: ", LOG_DEBUG);
+
+ if (argc > 1)
+ number = atoi(argv[1]);
+
+ assert(number > 0);
+ assertf(number > 1, "(B) the number was %d", number);
+
+ /* set up SIGABRT handler */
+ master = event_master_create("test");
+ signal_init(master, 0, NULL);
+
+ func_for_bt(number);
+ assert(number > 4);
+ assertf(number > 5, "(C) the number was %d", number);
+
+ assertf(number > 10, "(D) the number was %d", number);
+ return 0;
+}
diff --git a/tests/lib/test_assert.py b/tests/lib/test_assert.py
new file mode 100644
index 0000000..67c88e6
--- /dev/null
+++ b/tests/lib/test_assert.py
@@ -0,0 +1,56 @@
+import frrtest
+import os
+import re
+import subprocess
+import inspect
+
+basedir = os.path.dirname(__file__)
+program = os.path.join(basedir, "test_assert")
+
+
+def check(number, rex=None):
+ proc = subprocess.Popen(
+ [frrtest.binpath(program), str(number)],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
+ out, err = proc.communicate()
+ exitcode = proc.wait()
+
+ if rex is None:
+ assert exitcode == 0
+ else:
+ assert exitcode != 0
+
+ text = out.decode("US-ASCII") + err.decode("US-ASCII")
+ rex = re.compile(rex, re.M | re.S)
+ m = rex.search(text)
+ assert m is not None, "non-matching output: %s" % text
+
+
+def test_assert_0():
+ check(0, r"test_assert\.c:\d+.*number > 0")
+
+
+def test_assert_1():
+ check(1, r"test_assert\.c:\d+.*number > 1.*\(B\) the number was 1")
+
+
+def test_assert_2():
+ check(2, r"test_assert\.c:\d+.*number > 2")
+
+
+def test_assert_3():
+ check(3, r"test_assert\.c:\d+.*number > 3.*\(A\) the number was 3")
+
+
+def test_assert_4():
+ check(4, r"test_assert\.c:\d+.*number > 4")
+
+
+def test_assert_10():
+ check(10, r"test_assert\.c:\d+.*number > 10.*\(D\) the number was 10")
+
+
+def test_assert_11():
+ check(11)
diff --git a/tests/lib/test_atomlist.c b/tests/lib/test_atomlist.c
new file mode 100644
index 0000000..b50216c
--- /dev/null
+++ b/tests/lib/test_atomlist.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include "atomlist.h"
+#include "seqlock.h"
+#include "monotime.h"
+#include "printfrr.h"
+
+/*
+ * maybe test:
+ * - alist_del_hint
+ * - alist_next_safe
+ * - asort_del_hint
+ * - asort_next_safe
+ */
+
+static struct seqlock sqlo;
+
+PREDECL_ATOMLIST(alist);
+PREDECL_ATOMSORT_UNIQ(asort);
+struct item {
+ uint64_t val1;
+ struct alist_item chain;
+ struct asort_item sortc;
+ uint64_t val2;
+};
+DECLARE_ATOMLIST(alist, struct item, chain);
+
+static int icmp(const struct item *a, const struct item *b);
+DECLARE_ATOMSORT_UNIQ(asort, struct item, sortc, icmp);
+
+static int icmp(const struct item *a, const struct item *b)
+{
+ if (a->val1 > b->val1)
+ return 1;
+ if (a->val1 < b->val1)
+ return -1;
+ return 0;
+}
+
+#define NITEM 10000
+struct item itm[NITEM];
+
+static struct alist_head ahead;
+static struct asort_head shead;
+
+#define NTHREADS 4
+static struct testthread {
+ pthread_t pt;
+ struct seqlock sqlo;
+ size_t counter, nullops;
+} thr[NTHREADS];
+
+struct testrun {
+ struct testrun *next;
+ int lineno;
+ const char *desc;
+ ssize_t prefill;
+ bool sorted;
+ void (*func)(unsigned int offset);
+};
+struct testrun *runs = NULL;
+
+#define NOCLEAR -1
+
+#define deftestrun(name, _desc, _prefill, _sorted) \
+static void trfunc_##name(unsigned int offset); \
+struct testrun tr_##name = { \
+ .desc = _desc, \
+ .lineno = __LINE__, \
+ .prefill = _prefill, \
+ .func = &trfunc_##name, \
+ .sorted = _sorted }; \
+static void __attribute__((constructor)) trsetup_##name(void) \
+{ \
+ struct testrun **inspos = &runs; \
+ while (*inspos && (*inspos)->lineno < tr_##name.lineno) \
+ inspos = &(*inspos)->next; \
+ tr_##name.next = *inspos; \
+ *inspos = &tr_##name; \
+} \
+static void trfunc_##name(unsigned int offset) \
+{ \
+ size_t i = 0, n = 0;
+
+#define endtestrun \
+ thr[offset].counter = i; \
+ thr[offset].nullops = n; \
+}
+
+deftestrun(add, "add vs. add", 0, false)
+ for (; i < NITEM / NTHREADS; i++)
+ alist_add_head(&ahead, &itm[i * NTHREADS + offset]);
+endtestrun
+
+deftestrun(del, "del vs. del", NOCLEAR, false)
+ for (; i < NITEM / NTHREADS / 10; i++)
+ alist_del(&ahead, &itm[i * NTHREADS + offset]);
+endtestrun
+
+deftestrun(addtail, "add_tail vs. add_tail", 0, false)
+ for (; i < NITEM / NTHREADS; i++)
+ alist_add_tail(&ahead, &itm[i * NTHREADS + offset]);
+endtestrun
+
+deftestrun(pop, "pop vs. pop", NOCLEAR, false)
+ for (; i < NITEM / NTHREADS; )
+ if (alist_pop(&ahead))
+ i++;
+ else
+ n++;
+endtestrun
+
+deftestrun(headN_vs_pop1, "add_head(N) vs. pop(1)", 1, false);
+ if (offset == 0) {
+ struct item *dr = NULL;
+
+ for (i = n = 0; i < NITEM; ) {
+ dr = alist_pop(&ahead);
+ if (dr)
+ i++;
+ else
+ n++;
+ }
+ } else {
+ for (i = offset; i < NITEM; i += NTHREADS)
+ alist_add_head(&ahead, &itm[i]);
+ i = 0;
+ }
+endtestrun
+
+deftestrun(head1_vs_popN, "add_head(1) vs. pop(N)", 0, false);
+ if (offset < NTHREADS - 1) {
+ struct item *dr = NULL;
+
+ for (i = n = 0; i < NITEM / NTHREADS; ) {
+ dr = alist_pop(&ahead);
+ if (dr)
+ i++;
+ else
+ n++;
+ }
+ } else {
+ for (i = 0; i < NITEM; i++)
+ alist_add_head(&ahead, &itm[i]);
+ i = 0;
+ }
+endtestrun
+
+deftestrun(headN_vs_popN, "add_head(N) vs. pop(N)", NTHREADS / 2, false)
+ if (offset < NTHREADS / 2) {
+ struct item *dr = NULL;
+
+ for (i = n = 0; i < NITEM * 2 / NTHREADS; ) {
+ dr = alist_pop(&ahead);
+ if (dr)
+ i++;
+ else
+ n++;
+ }
+ } else {
+ for (i = offset; i < NITEM; i += NTHREADS)
+ alist_add_head(&ahead, &itm[i]);
+ i = 0;
+ }
+endtestrun
+
+deftestrun(tailN_vs_pop1, "add_tail(N) vs. pop(1)", 1, false)
+ if (offset == 0) {
+ struct item *dr = NULL;
+
+ for (i = n = 0; i < NITEM - (NITEM / NTHREADS); ) {
+ dr = alist_pop(&ahead);
+ if (dr)
+ i++;
+ else
+ n++;
+ }
+ } else {
+ for (i = offset; i < NITEM; i += NTHREADS)
+ alist_add_tail(&ahead, &itm[i]);
+ i = 0;
+ }
+endtestrun
+
+deftestrun(tail1_vs_popN, "add_tail(1) vs. pop(N)", 0, false)
+ if (offset < NTHREADS - 1) {
+ struct item *dr = NULL;
+
+ for (i = n = 0; i < NITEM / NTHREADS; ) {
+ dr = alist_pop(&ahead);
+ if (dr)
+ i++;
+ else
+ n++;
+ }
+ } else {
+ for (i = 0; i < NITEM; i++)
+ alist_add_tail(&ahead, &itm[i]);
+ i = 0;
+ }
+endtestrun
+
+deftestrun(sort_add, "add_sort vs. add_sort", 0, true)
+ for (; i < NITEM / NTHREADS / 10; i++)
+ asort_add(&shead, &itm[i * NTHREADS + offset]);
+endtestrun
+
+deftestrun(sort_del, "del_sort vs. del_sort", NOCLEAR, true)
+ for (; i < NITEM / NTHREADS / 10; i++)
+ asort_del(&shead, &itm[i * NTHREADS + offset]);
+endtestrun
+
+deftestrun(sort_add_del, "add_sort vs. del_sort", NTHREADS / 2, true)
+ if (offset < NTHREADS / 2) {
+ for (; i < NITEM / NTHREADS / 10; i++)
+ asort_del(&shead, &itm[i * NTHREADS + offset]);
+ } else {
+ for (; i < NITEM / NTHREADS / 10; i++)
+ asort_add(&shead, &itm[i * NTHREADS + offset]);
+ }
+endtestrun
+
+static void *thr1func(void *arg)
+{
+ struct testthread *p = arg;
+ unsigned int offset = (unsigned int)(p - &thr[0]);
+ seqlock_val_t sv;
+ struct testrun *tr;
+
+ for (tr = runs; tr; tr = tr->next) {
+ sv = seqlock_bump(&p->sqlo) - SEQLOCK_INCR;
+ seqlock_wait(&sqlo, sv);
+
+ tr->func(offset);
+ }
+ seqlock_bump(&p->sqlo);
+
+ return NULL;
+}
+
+static void clear_list(size_t prefill)
+{
+ size_t i;
+
+ memset(&ahead, 0, sizeof(ahead));
+ memset(&shead, 0, sizeof(shead));
+ memset(itm, 0, sizeof(itm));
+ for (i = 0; i < NITEM; i++) {
+ itm[i].val1 = itm[i].val2 = i;
+ if ((i % NTHREADS) < prefill) {
+ alist_add_tail(&ahead, &itm[i]);
+ asort_add(&shead, &itm[i]);
+ }
+ }
+}
+
+static void run_tr(struct testrun *tr)
+{
+ const char *desc = tr->desc;
+ struct timeval tv;
+ int64_t delta;
+ seqlock_val_t sv;
+ size_t c = 0, s = 0, n = 0;
+ struct item *item, *prev, dummy;
+
+ printfrr("[%02u] %35s %s\n", seqlock_cur(&sqlo) >> 2, "", desc);
+ fflush(stdout);
+
+ if (tr->prefill != NOCLEAR)
+ clear_list(tr->prefill);
+
+ monotime(&tv);
+ sv = seqlock_bump(&sqlo) - SEQLOCK_INCR;
+ for (size_t i = 0; i < NTHREADS; i++) {
+ seqlock_wait(&thr[i].sqlo, seqlock_cur(&sqlo));
+ s += thr[i].counter;
+ n += thr[i].nullops;
+ thr[i].counter = 0;
+ thr[i].nullops = 0;
+ }
+
+ delta = monotime_since(&tv, NULL);
+ if (tr->sorted) {
+ uint64_t prevval = 0;
+
+ frr_each(asort, &shead, item) {
+ assert(item->val1 >= prevval);
+ prevval = item->val1;
+ c++;
+ }
+ assert(c == asort_count(&shead));
+ } else {
+ prev = &dummy;
+ frr_each(alist, &ahead, item) {
+ assert(item != prev);
+ prev = item;
+ c++;
+ assert(c <= NITEM);
+ }
+ assert(c == alist_count(&ahead));
+ }
+ printfrr("\033[1A[%02u] %9"PRId64"us c=%5zu s=%5zu n=%5zu %s\n",
+ sv >> 2, delta, c, s, n, desc);
+}
+
+#ifdef BASIC_TESTS
+static void dump(const char *lbl)
+{
+ struct item *item, *safe;
+ size_t ctr = 0;
+
+ printfrr("dumping %s:\n", lbl);
+ frr_each_safe(alist, &ahead, item) {
+ printfrr("%s %3zu %p %3"PRIu64" %3"PRIu64"\n", lbl, ctr++,
+ (void *)item, item->val1, item->val2);
+ }
+}
+
+static void basic_tests(void)
+{
+ size_t i;
+
+ memset(&ahead, 0, sizeof(ahead));
+ memset(itm, 0, sizeof(itm));
+ for (i = 0; i < NITEM; i++)
+ itm[i].val1 = itm[i].val2 = i;
+
+ assert(alist_first(&ahead) == NULL);
+ dump("");
+ alist_add_head(&ahead, &itm[0]);
+ dump("");
+ alist_add_head(&ahead, &itm[1]);
+ dump("");
+ alist_add_tail(&ahead, &itm[2]);
+ dump("");
+ alist_add_tail(&ahead, &itm[3]);
+ dump("");
+ alist_del(&ahead, &itm[1]);
+ dump("");
+ printfrr("POP: %p\n", alist_pop(&ahead));
+ dump("");
+ printfrr("POP: %p\n", alist_pop(&ahead));
+ printfrr("POP: %p\n", alist_pop(&ahead));
+ printfrr("POP: %p\n", alist_pop(&ahead));
+ printfrr("POP: %p\n", alist_pop(&ahead));
+ dump("");
+}
+#else
+#define basic_tests() do { } while (0)
+#endif
+
+int main(int argc, char **argv)
+{
+ size_t i;
+
+ basic_tests();
+
+ seqlock_init(&sqlo);
+ seqlock_acquire_val(&sqlo, SEQLOCK_STARTVAL);
+
+ for (i = 0; i < NTHREADS; i++) {
+ seqlock_init(&thr[i].sqlo);
+ seqlock_acquire(&thr[i].sqlo, &sqlo);
+ thr[i].counter = 0;
+ thr[i].nullops = 0;
+
+ pthread_create(&thr[i].pt, NULL, thr1func, &thr[i]);
+ }
+
+ struct testrun *tr;
+
+ for (tr = runs; tr; tr = tr->next)
+ run_tr(tr);
+
+ for (i = 0; i < NTHREADS; i++)
+ pthread_join(thr[i].pt, NULL);
+
+ return 0;
+}
diff --git a/tests/lib/test_atomlist.py b/tests/lib/test_atomlist.py
new file mode 100644
index 0000000..719a2e7
--- /dev/null
+++ b/tests/lib/test_atomlist.py
@@ -0,0 +1,8 @@
+import frrtest
+
+
+class TestAtomlist(frrtest.TestMultiOut):
+ program = "./test_atomlist"
+
+
+TestAtomlist.exit_cleanly()
diff --git a/tests/lib/test_buffer.c b/tests/lib/test_buffer.c
new file mode 100644
index 0000000..bfb4600
--- /dev/null
+++ b/tests/lib/test_buffer.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2004 Paul Jakma
+ */
+
+#include <zebra.h>
+#include <memory.h>
+#include <lib_vty.h>
+#include <buffer.h>
+
+struct event_loop *master;
+
+int main(int argc, char **argv)
+{
+ struct buffer *b1, *b2;
+ int n;
+ char junk[3];
+ char c = 'a';
+
+ lib_cmd_init();
+
+ if ((argc != 2) || (sscanf(argv[1], "%d%1s", &n, junk) != 1)) {
+ fprintf(stderr, "Usage: %s <number of chars to simulate>\n",
+ *argv);
+ return 1;
+ }
+
+ b1 = buffer_new(0);
+ b2 = buffer_new(1024);
+
+ while (n-- > 0) {
+ buffer_put(b1, &c, 1);
+ buffer_put(b2, &c, 1);
+ if (c++ == 'z')
+ c = 'a';
+ buffer_reset(b1);
+ buffer_reset(b2);
+ }
+ buffer_free(b1);
+ buffer_free(b2);
+ return 0;
+}
diff --git a/tests/lib/test_checksum.c b/tests/lib/test_checksum.c
new file mode 100644
index 0000000..329ae60
--- /dev/null
+++ b/tests/lib/test_checksum.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2008 Sun Microsystems, Inc.
+ */
+
+#include <zebra.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "checksum.h"
+#include "network.h"
+#include "prng.h"
+
+struct event_loop *master;
+
+struct acc_vals {
+ int c0;
+ int c1;
+};
+
+struct csum_vals {
+ struct acc_vals a;
+ int x;
+ int y;
+};
+
+static struct csum_vals ospfd_vals, isisd_vals;
+
+typedef size_t testsz_t;
+typedef uint16_t testoff_t;
+
+/* Fletcher Checksum -- Refer to RFC1008. */
+#define MODX 4102U
+
+/* The final reduction phase.
+ * This one should be the original ospfd version
+ */
+static uint16_t reduce_ospfd(struct csum_vals *vals, testsz_t len,
+ testoff_t off)
+{
+#define x vals->x
+#define y vals->y
+#define c0 vals->a.c0
+#define c1 vals->a.c1
+
+ x = ((len - off - 1) * c0 - c1) % 255;
+
+ if (x <= 0)
+ x += 255;
+ y = 510 - c0 - x;
+ if (y > 255)
+ y -= 255;
+
+ /* take care endian issue. */
+ return htons((x << 8) + y);
+#undef x
+#undef y
+#undef c0
+#undef c1
+}
+
+/* slightly different concatenation */
+static uint16_t reduce_ospfd1(struct csum_vals *vals, testsz_t len,
+ testoff_t off)
+{
+#define x vals->x
+#define y vals->y
+#define c0 vals->a.c0
+#define c1 vals->a.c1
+
+ x = ((len - off - 1) * c0 - c1) % 255;
+ if (x <= 0)
+ x += 255;
+ y = 510 - c0 - x;
+ if (y > 255)
+ y -= 255;
+
+ /* take care endian issue. */
+ return htons((x << 8) | (y & 0xff));
+#undef x
+#undef y
+#undef c0
+#undef c1
+}
+
+/* original isisd version */
+static uint16_t reduce_isisd(struct csum_vals *vals, testsz_t len,
+ testoff_t off)
+{
+#define x vals->x
+#define y vals->y
+#define c0 vals->a.c0
+#define c1 vals->a.c1
+ uint32_t mul;
+
+ mul = (len - off) * (c0);
+ x = mul - c0 - c1;
+ y = c1 - mul - 1;
+
+ if (y > 0)
+ y++;
+ if (x < 0)
+ x--;
+
+ x %= 255;
+ y %= 255;
+
+ if (x == 0)
+ x = 255;
+ if (y == 0)
+ y = 1;
+
+ return htons((x << 8) | (y & 0xff));
+
+#undef x
+#undef y
+#undef c0
+#undef c1
+}
+
+/* Is the -1 in y wrong perhaps? */
+static uint16_t reduce_isisd_yfix(struct csum_vals *vals, testsz_t len,
+ testoff_t off)
+{
+#define x vals->x
+#define y vals->y
+#define c0 vals->a.c0
+#define c1 vals->a.c1
+ uint32_t mul;
+
+ mul = (len - off) * (c0);
+ x = mul - c0 - c1;
+ y = c1 - mul;
+
+ if (y > 0)
+ y++;
+ if (x < 0)
+ x--;
+
+ x %= 255;
+ y %= 255;
+
+ if (x == 0)
+ x = 255;
+ if (y == 0)
+ y = 1;
+
+ return htons((x << 8) | (y & 0xff));
+
+#undef x
+#undef y
+#undef c0
+#undef c1
+}
+
+/* Move the mods yp */
+static uint16_t reduce_isisd_mod(struct csum_vals *vals, testsz_t len,
+ testoff_t off)
+{
+#define x vals->x
+#define y vals->y
+#define c0 vals->a.c0
+#define c1 vals->a.c1
+ uint32_t mul;
+
+ mul = (len - off) * (c0);
+ x = mul - c1 - c0;
+ y = c1 - mul - 1;
+
+ x %= 255;
+ y %= 255;
+
+ if (y > 0)
+ y++;
+ if (x < 0)
+ x--;
+
+ if (x == 0)
+ x = 255;
+ if (y == 0)
+ y = 1;
+
+ return htons((x << 8) | (y & 0xff));
+
+#undef x
+#undef y
+#undef c0
+#undef c1
+}
+
+/* Move the mods up + fix y */
+static uint16_t reduce_isisd_mody(struct csum_vals *vals, testsz_t len,
+ testoff_t off)
+{
+#define x vals->x
+#define y vals->y
+#define c0 vals->a.c0
+#define c1 vals->a.c1
+ uint32_t mul;
+
+ mul = (len - off) * (c0);
+ x = mul - c0 - c1;
+ y = c1 - mul;
+
+ x %= 255;
+ y %= 255;
+
+ if (y > 0)
+ y++;
+ if (x < 0)
+ x--;
+
+ if (x == 0)
+ x = 255;
+ if (y == 0)
+ y = 1;
+
+ return htons((x << 8) | (y & 0xff));
+
+#undef x
+#undef y
+#undef c0
+#undef c1
+}
+
+struct reductions_t {
+ const char *name;
+ uint16_t (*f)(struct csum_vals *, testsz_t, testoff_t);
+} reducts[] = {
+ {.name = "ospfd", .f = reduce_ospfd},
+ {.name = "ospfd-1", .f = reduce_ospfd1},
+ {.name = "isisd", .f = reduce_isisd},
+ {.name = "isisd-yfix", .f = reduce_isisd_yfix},
+ {.name = "isisd-mod", .f = reduce_isisd_mod},
+ {.name = "isisd-mody", .f = reduce_isisd_mody},
+ {NULL, NULL},
+};
+
+/* The original ospfd checksum */
+static uint16_t ospfd_checksum(uint8_t *buffer, testsz_t len, testoff_t off)
+{
+ uint8_t *sp, *ep, *p, *q;
+ int c0 = 0, c1 = 0;
+ int x, y;
+ uint16_t checksum, *csum;
+
+ csum = (uint16_t *)(buffer + off);
+ *(csum) = 0;
+
+ sp = buffer;
+
+ for (ep = sp + len; sp < ep; sp = q) {
+ q = sp + MODX;
+ if (q > ep)
+ q = ep;
+ for (p = sp; p < q; p++) {
+ c0 += *p;
+ c1 += c0;
+ }
+ c0 %= 255;
+ c1 %= 255;
+ }
+
+ ospfd_vals.a.c0 = c0;
+ ospfd_vals.a.c1 = c1;
+
+ // printf ("%s: len %u, off %u, c0 %d, c1 %d\n",
+ // __func__, len, off, c0, c1);
+
+ x = ((int)(len - off - 1) * (int)c0 - (int)c1) % 255;
+
+ if (x <= 0)
+ x += 255;
+ y = 510 - c0 - x;
+ if (y > 255)
+ y -= 255;
+
+ ospfd_vals.x = x;
+ ospfd_vals.y = y;
+
+ buffer[off] = x;
+ buffer[off + 1] = y;
+
+ /* take care endian issue. */
+ checksum = htons((x << 8) | (y & 0xff));
+
+ return (checksum);
+}
+
+/* the original, broken isisd checksum */
+static uint16_t iso_csum_create(uint8_t *buffer, testsz_t len, testoff_t off)
+{
+
+ uint8_t *p;
+ int x;
+ int y;
+ uint32_t mul;
+ uint32_t c0;
+ uint32_t c1;
+ uint16_t checksum, *csum;
+ int i, init_len, partial_len;
+
+ checksum = 0;
+
+ csum = (uint16_t *)(buffer + off);
+ *(csum) = checksum;
+
+ p = buffer;
+ c0 = 0;
+ c1 = 0;
+ init_len = len;
+
+ while (len != 0) {
+ partial_len = MIN(len, MODX);
+
+ for (i = 0; i < partial_len; i++) {
+ c0 = c0 + *(p++);
+ c1 += c0;
+ }
+
+ c0 = c0 % 255;
+ c1 = c1 % 255;
+
+ len -= partial_len;
+ }
+
+ isisd_vals.a.c0 = c0;
+ isisd_vals.a.c1 = c1;
+
+ mul = (init_len - off) * c0;
+
+ x = mul - c1 - c0;
+ y = c1 - mul - 1;
+
+ if (y > 0)
+ y++;
+ if (x < 0)
+ x--;
+
+ x %= 255;
+ y %= 255;
+
+ if (x == 0)
+ x = 255;
+ if (y == 0)
+ y = 1;
+
+ isisd_vals.x = x;
+ isisd_vals.y = y;
+
+ checksum = htons((x << 8) | (y & 0xFF));
+
+ *(csum) = checksum;
+
+ /* return the checksum for user usage */
+ return checksum;
+}
+
+static int verify(uint8_t *buffer, testsz_t len)
+{
+ uint8_t *p;
+ uint32_t c0;
+ uint32_t c1;
+ int i, partial_len;
+
+ p = buffer;
+
+ c0 = 0;
+ c1 = 0;
+
+ while (len) {
+ partial_len = MIN(len, 5803U);
+
+ for (i = 0; i < partial_len; i++) {
+ c0 = c0 + *(p++);
+ c1 += c0;
+ }
+ c0 = c0 % 255;
+ c1 = c1 % 255;
+
+ len -= partial_len;
+ }
+
+ if (c0 == 0 && c1 == 0)
+ return 0;
+
+ return 1;
+}
+
+static int /* return checksum in low-order 16 bits */
+ in_cksum_optimized(void *parg, int nbytes)
+{
+ unsigned short *ptr = parg;
+ register long sum; /* assumes long == 32 bits */
+ register unsigned short answer; /* assumes unsigned short == 16 bits */
+ register int count;
+ /*
+ * Our algorithm is simple, using a 32-bit accumulator (sum),
+ * we add sequential 16-bit words to it, and at the end, fold back
+ * all the carry bits from the top 16 bits into the lower 16 bits.
+ */
+
+ sum = 0;
+ count = nbytes >> 1; /* div by 2 */
+ for (ptr--; count; --count)
+ sum += *++ptr;
+
+ if (nbytes & 1) /* Odd */
+ sum += *(uint8_t *)(++ptr); /* one byte only */
+
+ /*
+ * Add back carry outs from top 16 bits to low 16 bits.
+ */
+
+ sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* ones-complement, then truncate to 16 bits */
+ return (answer);
+}
+
+
+static int /* return checksum in low-order 16 bits */
+ in_cksum_rfc(void *parg, int count)
+/* from RFC 1071 */
+{
+ unsigned short *addr = parg;
+ /* Compute Internet Checksum for "count" bytes
+ * beginning at location "addr".
+ */
+ register long sum = 0;
+
+ while (count > 1) {
+ /* This is the inner loop */
+ sum += *addr++;
+ count -= 2;
+ }
+ /* Add left-over byte, if any */
+ if (count > 0) {
+ sum += *(uint8_t *)addr;
+ }
+
+ /* Fold 32-bit sum to 16 bits */
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+ return ~sum;
+}
+
+
+int main(int argc, char **argv)
+{
+/* 60017 65629 702179 */
+#define MAXDATALEN 60017
+#define BUFSIZE MAXDATALEN + sizeof(uint16_t)
+ uint8_t buffer[BUFSIZE];
+ int exercise = 0;
+#define EXERCISESTEP 257
+ struct prng *prng = prng_new(0);
+
+ while (1) {
+ uint16_t ospfd, isisd, lib, in_csum, in_csum_res, in_csum_rfc;
+ int i;
+
+ exercise += EXERCISESTEP;
+ exercise %= MAXDATALEN;
+
+ printf("\rexercising length %d\033[K", exercise);
+
+ for (i = 0; i < exercise; i++)
+ buffer[i] = prng_rand(prng);
+
+ in_csum = in_cksum(buffer, exercise);
+ in_csum_res = in_cksum_optimized(buffer, exercise);
+ in_csum_rfc = in_cksum_rfc(buffer, exercise);
+ if (in_csum_res != in_csum || in_csum != in_csum_rfc)
+ printf("\nverify: in_chksum failed in_csum:%x, in_csum_res:%x,in_csum_rfc %x, len:%d\n",
+ in_csum, in_csum_res, in_csum_rfc, exercise);
+
+ struct iovec iov[3];
+ uint16_t in_csum_iov;
+
+ iov[0].iov_base = buffer;
+ iov[0].iov_len = exercise / 2;
+ iov[1].iov_base = buffer + iov[0].iov_len;
+ iov[1].iov_len = exercise - iov[0].iov_len;
+
+ in_csum_iov = in_cksumv(iov, 2);
+ if (in_csum_iov != in_csum)
+ printf("\nverify: in_cksumv failed, lens: %zu+%zu\n",
+ iov[0].iov_len, iov[1].iov_len);
+
+ if (exercise >= 6) {
+ /* force split with byte leftover */
+ iov[0].iov_base = buffer;
+ iov[0].iov_len = (exercise / 2) | 1;
+ iov[1].iov_base = buffer + iov[0].iov_len;
+ iov[1].iov_len = 2;
+ iov[2].iov_base = buffer + iov[0].iov_len + 2;
+ iov[2].iov_len = exercise - iov[0].iov_len - 2;
+
+ in_csum_iov = in_cksumv(iov, 3);
+ if (in_csum_iov != in_csum)
+ printf("\nverify: in_cksumv failed, lens: %zu+%zu+%zu, got %04x, expected %04x\n",
+ iov[0].iov_len, iov[1].iov_len,
+ iov[2].iov_len, in_csum_iov, in_csum);
+
+ /* force split without byte leftover */
+ iov[0].iov_base = buffer;
+ iov[0].iov_len = (exercise / 2) & ~1UL;
+ iov[1].iov_base = buffer + iov[0].iov_len;
+ iov[1].iov_len = 2;
+ iov[2].iov_base = buffer + iov[0].iov_len + 2;
+ iov[2].iov_len = exercise - iov[0].iov_len - 2;
+
+ in_csum_iov = in_cksumv(iov, 3);
+ if (in_csum_iov != in_csum)
+ printf("\nverify: in_cksumv failed, lens: %zu+%zu+%zu, got %04x, expected %04x\n",
+ iov[0].iov_len, iov[1].iov_len,
+ iov[2].iov_len, in_csum_iov, in_csum);
+ }
+
+ if (exercise >= FLETCHER_CHECKSUM_VALIDATE)
+ continue;
+
+ ospfd = ospfd_checksum(buffer, exercise + sizeof(uint16_t),
+ exercise);
+ if (verify(buffer, exercise + sizeof(uint16_t)))
+ printf("\nverify: ospfd failed\n");
+ isisd = iso_csum_create(buffer, exercise + sizeof(uint16_t),
+ exercise);
+ if (verify(buffer, exercise + sizeof(uint16_t)))
+ printf("\nverify: isisd failed\n");
+ lib = fletcher_checksum(buffer, exercise + sizeof(uint16_t),
+ exercise);
+ if (verify(buffer, exercise + sizeof(uint16_t)))
+ printf("\nverify: lib failed\n");
+
+ if (ospfd != lib) {
+ printf("\nMismatch in values at size %d\n"
+ "ospfd: 0x%04x\tc0: %d\tc1: %d\tx: %d\ty: %d\n"
+ "isisd: 0x%04x\tc0: %d\tc1: %d\tx: %d\ty: %d\n"
+ "lib: 0x%04x\n",
+ exercise, ospfd, ospfd_vals.a.c0,
+ ospfd_vals.a.c1, ospfd_vals.x, ospfd_vals.y,
+ isisd, isisd_vals.a.c0, isisd_vals.a.c1,
+ isisd_vals.x, isisd_vals.y, lib);
+
+ /* Investigate reduction phase discrepencies */
+ if (ospfd_vals.a.c0 == isisd_vals.a.c0
+ && ospfd_vals.a.c1 == isisd_vals.a.c1) {
+ printf("\n");
+ for (i = 0; reducts[i].name != NULL; i++) {
+ ospfd = reducts[i].f(
+ &ospfd_vals,
+ exercise + sizeof(uint16_t),
+ exercise);
+ printf("%20s: x: %02x, y %02x, checksum 0x%04x\n",
+ reducts[i].name,
+ ospfd_vals.x & 0xff,
+ ospfd_vals.y & 0xff, ospfd);
+ }
+ }
+
+ printf("\n uint8_t testdata [] = {\n ");
+ for (i = 0; i < exercise; i++) {
+ printf("0x%02x,%s", buffer[i],
+ (i + 1) % 8 ? " " : "\n ");
+ }
+ printf("\n}\n");
+ exit(1);
+ }
+ }
+}
diff --git a/tests/lib/test_darr.c b/tests/lib/test_darr.c
new file mode 100644
index 0000000..9150aed
--- /dev/null
+++ b/tests/lib/test_darr.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * June 23 2023, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (c) 2023, LabN Consulting, L.L.C.
+ *
+ */
+#include <zebra.h>
+#include "darr.h"
+
+/*
+ * Public functions to test:
+ * [x] - darr_append
+ * [x] - darr_append_n
+ * [x] - darr_append_nz
+ * [x] - darr_cap
+ * [-] - darr_ensure_cap
+ * [x] - darr_ensure_i
+ * [x] - darr_foreach_i
+ * [x] - darr_foreach_p
+ * [x] - darr_free
+ * [x] - darr_insert
+ * [ ] - darr_insertz
+ * [x] - darr_insert_n
+ * [x] - darr_insert_nz
+ * [x] - darr_maxi
+ * [x] - darr_pop
+ * [x] - darr_push
+ * [ ] - darr_pushz
+ * [x] - darr_remove
+ * [x] - darr_remove_n
+ * [x] - darr_reset
+ * [x] - darr_setlen
+ */
+
+static void test_int(void)
+{
+ int z105[105] = {0};
+ int a1[] = {0, 1, 2, 3, 4};
+ int a2[] = {4, 3, 2, 1, 0};
+ int *da1 = NULL;
+ int *da2 = NULL;
+ int *dap;
+ uint i;
+
+ darr_ensure_i(da1, 0);
+ da1[0] = 0;
+ assert(darr_len(da1) == 1);
+ assert(darr_cap(da1) == 1);
+
+ *darr_ensure_i(da1, 1) = 1;
+ assert(darr_len(da1) == 2);
+ assert(darr_cap(da1) == 2);
+
+ darr_ensure_i(da1, 4);
+ darr_foreach_i (da1, i)
+ da1[i] = i;
+
+ assert(darr_len(da1) == 5);
+ /* minimum non-pow2 array size for long long and smaller */
+ assert(darr_cap(da1) == 8);
+ assert(!memcmp(da1, a1, sizeof(a1)));
+
+ /* reverse the numbers */
+ darr_foreach_p (da1, dap)
+ *dap = darr_end(da1) - dap - 1;
+ assert(!memcmp(da1, a2, sizeof(a2)));
+
+ darr_append_n(da1, 100);
+ darr_foreach_p (da1, dap)
+ *dap = darr_end(da1) - dap - 1;
+
+ darr_pop_n(da1, 100);
+ darr_append_nz(da1, 100);
+ assert(!memcmp(&da1[5], z105, _darr_esize(da1) * 100));
+
+ assert(darr_len(da1) == 105);
+ assert(darr_maxi(da1) == 127);
+ assert(darr_cap(da1) == 128);
+
+ darr_setlen(da1, 102);
+ assert(darr_len(da1) == 102);
+ assert(darr_maxi(da1) == 127);
+
+ int a3[] = { 0xdeadbeaf, 0x12345678 };
+
+ da1[0] = a3[0];
+ da1[101] = a3[1];
+ darr_remove_n(da1, 1, 100);
+ assert(darr_len(da1) == array_size(a3));
+ assert(!memcmp(da1, a3, sizeof(a3)));
+
+ da1[0] = a3[1];
+ da1[1] = a3[0];
+
+ darr_insert_n(da1, 1, 100);
+ assert(darr_len(da1) == 102);
+ assert(da1[0] == a3[1]);
+ assert(da1[101] == a3[0]);
+
+ darr_reset(da1);
+ assert(darr_len(da1) == 0);
+ assert(darr_maxi(da1) == 127);
+ assert(darr_cap(da1) == 128);
+
+ /* we touch the length field of the freed block here somehow */
+ darr_insert_n(da1, 100, 300);
+ assert(darr_len(da1) == 400);
+ assert(darr_cap(da1) == 512);
+
+ da1[400 - 1] = 0x0BAD;
+ *darr_insert(da1, 0) = 0xF00D;
+ assert(da1[0] == 0xF00D);
+ assert(da1[400] == 0x0BAD);
+ assert(darr_len(da1) == 401);
+ assert(darr_cap(da1) == 512);
+
+ darr_free(da1);
+ assert(da1 == NULL);
+ assert(darr_len(da1) == 0);
+ darr_setlen(da1, 0);
+ darr_reset(da1);
+ darr_free(da1);
+
+ *darr_append(da2) = 0;
+ *darr_append(da2) = 1;
+ darr_push(da2, 2);
+ darr_push(da2, 3);
+ darr_push(da2, 4);
+
+ assert(!memcmp(da2, a1, sizeof(a1)));
+
+ assert(darr_pop(da2) == 4);
+ assert(darr_pop(da2) == 3);
+ assert(darr_pop(da2) == 2);
+ assert(darr_len(da2) == 2);
+ assert(darr_pop(da2) == 1);
+ assert(darr_pop(da2) == 0);
+ assert(darr_len(da2) == 0);
+
+ darr_free(da2);
+}
+
+static void test_struct(void)
+{
+ /*
+ *uwould like to use different sizes with padding but memcmp can't be
+ *used then.
+ */
+ struct st {
+ long long a;
+ long long b;
+ };
+ struct st z102[102] = {{0, 0}};
+ struct st *da1 = NULL;
+ struct st *da2 = NULL;
+ struct st a1[] = {
+ {0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4},
+ };
+ uint i;
+
+ darr_ensure_i(da1, 0);
+ da1[0].a = 0;
+ da1[0].b = 0;
+ assert(darr_len(da1) == 1);
+ assert(darr_cap(da1) == 1);
+
+ darr_ensure_i(da1, 1)->a = 1;
+ darr_ensure_i(da1, 1)->b = 1;
+ assert(darr_len(da1) == 2);
+ assert(darr_cap(da1) == 2);
+
+ darr_ensure_i(da1, 4);
+ da1[2].a = 2;
+ da1[2].b = 2;
+
+ da1[3].a = 3;
+ da1[3].b = 3;
+
+ da1[4].a = 4;
+ da1[4].b = 4;
+
+ assert(darr_len(da1) == 5);
+ /* minimum non-pow2 array size for long long and smaller */
+ assert(darr_cap(da1) == 8);
+ assert(!memcmp(da1, a1, sizeof(a1)));
+
+ darr_append_n(da1, 100);
+
+ assert(darr_len(da1) == 105);
+ assert(darr_maxi(da1) == 127);
+ assert(darr_cap(da1) == 128);
+
+ darr_setlen(da1, 102);
+ assert(darr_len(da1) == 102);
+ assert(darr_maxi(da1) == 127);
+
+ struct st a2[] = {
+ {0xdeadbeaf, 0xdeadbeaf},
+ {0x12345678, 0x12345678},
+ };
+ da1[0] = a2[0];
+ da1[101] = a2[1];
+ darr_remove_n(da1, 1, 100);
+ assert(darr_len(da1) == array_size(a2));
+ assert(!memcmp(da1, a2, sizeof(a2)));
+
+ da1[0] = a2[1];
+ da1[1] = a2[0];
+
+ darr_insert_n(da1, 1, 100);
+ assert(darr_len(da1) == 102);
+ darr_foreach_i (da1, i) {
+ da1[i].a = i;
+ da1[i].b = i;
+ }
+ darr_remove_n(da1, 1, 100);
+ assert(darr_len(da1) == 2);
+ darr_insert_nz(da1, 1, 100);
+ assert(!memcmp(&da1[1], z102, 100 * sizeof(da1[0])));
+ /* assert(da1[0] == a2[1]); */
+ /* assert(da1[101] == a2[0]); */
+
+ darr_reset(da1);
+ assert(darr_len(da1) == 0);
+ assert(darr_maxi(da1) == 127);
+ assert(darr_cap(da1) == 128);
+
+ /* we touch the length field of the freed block here somehow */
+ darr_insert_n(da1, 100, 300);
+
+ assert(darr_len(da1) == 400);
+ assert(darr_cap(da1) == 512);
+
+ darr_free(da1);
+ assert(da1 == NULL);
+
+ assert(darr_len(da1) == 0);
+ darr_setlen(da1, 0);
+ darr_reset(da1);
+
+ darr_free(da1);
+
+ struct st i0 = {0, 0};
+ struct st i1 = {1, 1};
+ struct st i2 = {2, 2};
+ struct st i3 = {3, 3};
+ struct st i4 = {4, 4};
+
+ *darr_append(da2) = i0;
+ *darr_append(da2) = i1;
+ darr_push(da2, i2);
+ darr_push(da2, i3);
+ darr_push(da2, i4);
+
+ assert(!memcmp(da2, a1, sizeof(a1)));
+
+ struct st p0, p1, p2, p3, p4;
+
+ p4 = darr_pop(da2);
+ p3 = darr_pop(da2);
+ p2 = darr_pop(da2);
+ p1 = darr_pop(da2);
+ p0 = darr_pop(da2);
+ assert(darr_len(da2) == 0);
+ assert(p4.a == i4.a && p4.b == i4.b);
+ assert(p3.a == i3.a && p3.b == i3.b);
+ assert(p2.a == i2.a && p2.b == i2.b);
+ assert(p1.a == i1.a && p1.b == i1.b);
+ assert(p0.a == i0.a && p0.b == i0.b);
+
+ darr_free(da2);
+}
+
+int main(int argc, char **argv)
+{
+ test_int();
+ test_struct();
+}
diff --git a/tests/lib/test_frrlua.c b/tests/lib/test_frrlua.c
new file mode 100644
index 0000000..701e171
--- /dev/null
+++ b/tests/lib/test_frrlua.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * frrlua unit tests
+ * Copyright (C) 2021 Donald Lee
+ */
+
+#include <zebra.h>
+#include "string.h"
+#include "stdio.h"
+#include "lib/frrlua.h"
+
+static void test_encode_decode(void)
+{
+ lua_State *L = luaL_newstate();
+
+ long long a = 123;
+ long long b = a;
+
+ lua_pushintegerp(L, &a);
+ lua_decode_integerp(L, -1, &a);
+ assert(a == b);
+ assert(lua_gettop(L) == 0);
+
+ time_t time_a = 100;
+ time_t time_b;
+
+ lua_pushinteger(L, time_a);
+ time_b = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ assert(time_a == time_b);
+ assert(lua_gettop(L) == 0);
+
+ char str_b[] = "Hello", str_a[6];
+
+ strlcpy(str_a, str_b, sizeof(str_b));
+ lua_pushstring_wrapper(L, str_a);
+ lua_decode_stringp(L, -1, str_a);
+ assert(strncmp(str_a, str_b, sizeof(str_b)) == 0);
+ assert(lua_gettop(L) == 0);
+
+ char p_b_str[] = "10.0.0.0/24", p_a_str[12];
+ struct prefix p_a;
+
+ strlcpy(p_a_str, p_b_str, sizeof(p_b_str));
+ str2prefix(p_a_str, &p_a);
+ lua_pushprefix(L, &p_a);
+ lua_decode_prefix(L, -1, &p_a);
+ prefix2str(&p_a, p_a_str, sizeof(p_b_str));
+ assert(strncmp(p_a_str, p_b_str, sizeof(p_b_str)) == 0);
+ assert(lua_gettop(L) == 0);
+
+ struct interface ifp_a = {};
+ struct interface ifp_b = ifp_a;
+
+ lua_pushinterface(L, &ifp_a);
+ lua_decode_interface(L, -1, &ifp_a);
+ assert(strncmp(ifp_a.name, ifp_b.name, sizeof(ifp_b.name)) == 0);
+ assert(ifp_a.ifindex == ifp_b.ifindex);
+ assert(ifp_a.status == ifp_b.status);
+ assert(ifp_a.flags == ifp_b.flags);
+ assert(ifp_a.metric == ifp_b.metric);
+ assert(ifp_a.speed == ifp_b.speed);
+ assert(ifp_a.mtu == ifp_b.mtu);
+ assert(ifp_a.mtu6 == ifp_b.mtu6);
+ assert(ifp_a.bandwidth == ifp_b.bandwidth);
+ assert(ifp_a.link_ifindex == ifp_b.link_ifindex);
+ assert(ifp_a.ll_type == ifp_b.ll_type);
+ assert(lua_gettop(L) == 0);
+
+ struct in_addr addr_a = {};
+ struct in_addr addr_b = addr_a;
+
+ lua_pushinaddr(L, &addr_a);
+ lua_decode_inaddr(L, -1, &addr_a);
+ assert(addr_a.s_addr == addr_b.s_addr);
+ assert(lua_gettop(L) == 0);
+
+ struct in6_addr in6addr_a = {};
+ struct in6_addr in6addr_b = in6addr_a;
+
+ lua_pushin6addr(L, &in6addr_a);
+ lua_decode_in6addr(L, -1, &in6addr_a);
+ assert(in6addr_cmp(&in6addr_a, &in6addr_b) == 0);
+ assert(lua_gettop(L) == 0);
+
+ union sockunion su_a, su_b;
+
+ memset(&su_a, 0, sizeof(union sockunion));
+ memset(&su_b, 0, sizeof(union sockunion));
+ lua_pushsockunion(L, &su_a);
+ lua_decode_sockunion(L, -1, &su_a);
+ assert(sockunion_cmp(&su_a, &su_b) == 0);
+ assert(lua_gettop(L) == 0);
+}
+
+int main(int argc, char **argv)
+{
+ test_encode_decode();
+}
diff --git a/tests/lib/test_frrlua.py b/tests/lib/test_frrlua.py
new file mode 100644
index 0000000..2f6ddc1
--- /dev/null
+++ b/tests/lib/test_frrlua.py
@@ -0,0 +1,14 @@
+import frrtest
+import pytest
+
+if 'S["SCRIPTING_TRUE"]=""\n' not in open("../config.status").readlines():
+ class TestFrrlua:
+ @pytest.mark.skipif(True, reason="Test unsupported")
+ def test_exit_cleanly(self):
+ pass
+else:
+
+ class TestFrrlua(frrtest.TestMultiOut):
+ program = "./test_frrlua"
+
+ TestFrrlua.exit_cleanly()
diff --git a/tests/lib/test_frrscript.c b/tests/lib/test_frrscript.c
new file mode 100644
index 0000000..7d4746c
--- /dev/null
+++ b/tests/lib/test_frrscript.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * frrscript unit tests
+ * Copyright (C) 2021 Donald Lee
+ */
+
+#include <zebra.h>
+
+#include "lib/frrscript.h"
+#include "lib/frrlua.h"
+
+int main(int argc, char **argv)
+{
+ frrscript_init("./lib");
+ struct frrscript *fs = frrscript_new("script1");
+ int result;
+
+ /* Positive testing */
+
+ long long a = 100, b = 200;
+
+ result = frrscript_load(fs, "foo", NULL);
+ assert(result == 0);
+ result = frrscript_call(fs, "foo", ("a", &a), ("b", &b));
+ assert(result == 0);
+ assert(a == 101);
+ assert(b == 201);
+
+ a = 100, b = 200;
+
+ result = frrscript_load(fs, "bar", NULL);
+ assert(result == 0);
+ result = frrscript_call(fs, "bar", ("a", &a), ("b", &b));
+ assert(result == 0);
+ long long *cptr = frrscript_get_result(fs, "bar", "c", lua_tointegerp);
+
+ /* a should not occur in the returned table in script */
+ assert(a == 100);
+ assert(b == 201);
+ assert(*cptr == 303);
+ XFREE(MTYPE_SCRIPT_RES, cptr);
+
+ long long n = 5;
+
+ result = frrscript_load(fs, "fact", NULL);
+ assert(result == 0);
+ result = frrscript_call(fs, "fact", ("n", &n));
+ assert(result == 0);
+ long long *ansptr =
+ frrscript_get_result(fs, "fact", "ans", lua_tointegerp);
+ assert(*ansptr == 120);
+
+ /* check consecutive call + get_result without re-loading */
+ n = 4;
+ result = frrscript_call(fs, "fact", ("n", &n));
+ assert(result == 0);
+ ansptr = frrscript_get_result(fs, "fact", "ans", lua_tointegerp);
+ assert(*ansptr == 24);
+
+ XFREE(MTYPE_SCRIPT_RES, ansptr);
+
+ /* Negative testing */
+
+ /* Function does not exist in script file*/
+ result = frrscript_load(fs, "does_not_exist", NULL);
+ assert(result == 1);
+
+ /* Function was not (successfully) loaded */
+ result = frrscript_call(fs, "does_not_exist", ("a", &a), ("b", &b));
+ assert(result == 1);
+
+ /* Get result from a function that was not loaded */
+ long long *llptr =
+ frrscript_get_result(fs, "does_not_exist", "c", lua_tointegerp);
+ assert(llptr == NULL);
+
+ /* Function returns void */
+ result = frrscript_call(fs, "bad_return1");
+ assert(result == 1);
+
+ /* Function returns number */
+ result = frrscript_call(fs, "bad_return2");
+ assert(result == 1);
+
+ /* Get non-existent result from a function */
+ result = frrscript_call(fs, "bad_return3");
+ assert(result == 1);
+ long long *cllptr =
+ frrscript_get_result(fs, "bad_return3", "c", lua_tointegerp);
+ assert(cllptr == NULL);
+
+ /* Function throws exception */
+ result = frrscript_call(fs, "bad_return4");
+ assert(result == 1);
+
+ frrscript_delete(fs);
+
+ return 0;
+}
diff --git a/tests/lib/test_frrscript.py b/tests/lib/test_frrscript.py
new file mode 100644
index 0000000..046d97b
--- /dev/null
+++ b/tests/lib/test_frrscript.py
@@ -0,0 +1,14 @@
+import frrtest
+import pytest
+
+if 'S["SCRIPTING_TRUE"]=""\n' not in open("../config.status").readlines():
+ class TestFrrscript:
+ @pytest.mark.skipif(True, reason="Test unsupported")
+ def test_exit_cleanly(self):
+ pass
+else:
+
+ class TestFrrscript(frrtest.TestMultiOut):
+ program = "./test_frrscript"
+
+ TestFrrscript.exit_cleanly()
diff --git a/tests/lib/test_graph.c b/tests/lib/test_graph.c
new file mode 100644
index 0000000..86af02a
--- /dev/null
+++ b/tests/lib/test_graph.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Test graph data structure.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Quentin Young
+ */
+#include <zebra.h>
+#include <graph.h>
+#include <memory.h>
+#include <buffer.h>
+
+#define NUMNODES 32
+
+static void graph_custom_print_cb(struct graph_node *gn, struct buffer *buf)
+{
+ char nbuf[64];
+ char *gname = gn->data;
+
+ for (unsigned int i = 0; i < vector_active(gn->to); i++) {
+ struct graph_node *adj = vector_slot(gn->to, i);
+ char *name = adj->data;
+
+ snprintf(nbuf, sizeof(nbuf), " n%s -> n%s;\n", gname, name);
+ buffer_putstr(buf, nbuf);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct graph *g = graph_new();
+ struct graph_node *gn[NUMNODES];
+ char names[NUMNODES][16];
+
+ /* create vertices */
+ for (unsigned int i = 0; i < NUMNODES; i++) {
+ snprintf(names[i], sizeof(names[i]), "%u", i);
+ gn[i] = graph_new_node(g, names[i], NULL);
+ }
+
+ /* create edges */
+ for (unsigned int i = 1; i < NUMNODES - 1; i++) {
+ graph_add_edge(gn[0], gn[i]);
+ graph_add_edge(gn[i], gn[i + 1]);
+ }
+ graph_add_edge(gn[0], gn[NUMNODES - 1]);
+ graph_add_edge(gn[NUMNODES - 1], gn[1]);
+
+ /* print DOT */
+ char *dumped = graph_dump_dot(g, gn[0], graph_custom_print_cb);
+
+ fprintf(stdout, "%s", dumped);
+ XFREE(MTYPE_TMP, dumped);
+
+ /* remove some edges */
+ for (unsigned int i = NUMNODES - 1; i > NUMNODES / 2; --i)
+ for (unsigned int j = 0; j < NUMNODES; j++)
+ graph_remove_edge(gn[i], gn[j]);
+
+ /* remove some nodes */
+ for (unsigned int i = 0; i < NUMNODES / 2; i++)
+ graph_delete_node(g, gn[i]);
+
+ graph_delete_graph(g);
+}
diff --git a/tests/lib/test_graph.py b/tests/lib/test_graph.py
new file mode 100644
index 0000000..b26986c
--- /dev/null
+++ b/tests/lib/test_graph.py
@@ -0,0 +1,5 @@
+import frrtest
+
+
+class TestGraph(frrtest.TestRefOut):
+ program = "./test_graph"
diff --git a/tests/lib/test_graph.refout b/tests/lib/test_graph.refout
new file mode 100644
index 0000000..955f552
--- /dev/null
+++ b/tests/lib/test_graph.refout
@@ -0,0 +1,64 @@
+digraph {
+ n0 -> n1;
+ n0 -> n2;
+ n0 -> n3;
+ n0 -> n4;
+ n0 -> n5;
+ n0 -> n6;
+ n0 -> n7;
+ n0 -> n8;
+ n0 -> n9;
+ n0 -> n10;
+ n0 -> n11;
+ n0 -> n12;
+ n0 -> n13;
+ n0 -> n14;
+ n0 -> n15;
+ n0 -> n16;
+ n0 -> n17;
+ n0 -> n18;
+ n0 -> n19;
+ n0 -> n20;
+ n0 -> n21;
+ n0 -> n22;
+ n0 -> n23;
+ n0 -> n24;
+ n0 -> n25;
+ n0 -> n26;
+ n0 -> n27;
+ n0 -> n28;
+ n0 -> n29;
+ n0 -> n30;
+ n0 -> n31;
+ n31 -> n1;
+ n1 -> n2;
+ n2 -> n3;
+ n3 -> n4;
+ n4 -> n5;
+ n5 -> n6;
+ n6 -> n7;
+ n7 -> n8;
+ n8 -> n9;
+ n9 -> n10;
+ n10 -> n11;
+ n11 -> n12;
+ n12 -> n13;
+ n13 -> n14;
+ n14 -> n15;
+ n15 -> n16;
+ n16 -> n17;
+ n17 -> n18;
+ n18 -> n19;
+ n19 -> n20;
+ n20 -> n21;
+ n21 -> n22;
+ n22 -> n23;
+ n23 -> n24;
+ n24 -> n25;
+ n25 -> n26;
+ n26 -> n27;
+ n27 -> n28;
+ n28 -> n29;
+ n29 -> n30;
+ n30 -> n31;
+}
diff --git a/tests/lib/test_grpc.cpp b/tests/lib/test_grpc.cpp
new file mode 100644
index 0000000..957ffde
--- /dev/null
+++ b/tests/lib/test_grpc.cpp
@@ -0,0 +1,977 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * May 16 2021, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (c) 2021, LabN Consulting, L.L.C
+ */
+
+#include <time.h>
+#include <unistd.h>
+#include <zebra.h>
+
+#include "filter.h"
+#include "frr_pthread.h"
+#include "libfrr.h"
+#include "routing_nb.h"
+#include "northbound_cli.h"
+#include "frrevent.h"
+#include "vrf.h"
+#include "vty.h"
+
+#include "staticd/static_debug.h"
+#include "staticd/static_nb.h"
+#include "staticd/static_vrf.h"
+#include "staticd/static_vty.h"
+#include "staticd/static_zebra.h"
+
+// GRPC C++ includes
+#include <string>
+#include <sstream>
+#include <grpc/grpc.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/client_context.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/security/credentials.h>
+#include "grpc/frr-northbound.grpc.pb.h"
+
+DEFINE_HOOK(test_grpc_late_init, (struct event_loop * tm), (tm));
+DEFINE_KOOH(test_grpc_fini, (), ());
+
+struct vty *vty;
+
+bool mpls_enabled;
+struct event_loop *master;
+struct zebra_privs_t static_privs = {0};
+struct frrmod_runtime *grpc_module;
+char binpath[2 * MAXPATHLEN + 1];
+
+extern const char *json_expect1;
+extern const char *json_expect2;
+extern const char *json_expect3;
+extern const char *json_loadconf1;
+
+int test_dbg = 1;
+
+void inline test_debug(const std::string &s)
+{
+ if (test_dbg)
+ std::cout << s << std::endl;
+}
+
+// static struct option_chain modules[] = {{ .arg = "grpc:50051" }]
+// static struct option_chain **modnext = modules->next;
+
+static const struct frr_yang_module_info *const staticd_yang_modules[] = {
+ &frr_interface_info, &frr_filter_info, &frr_routing_info,
+ &frr_staticd_info, &frr_vrf_info,
+};
+
+static void grpc_thread_stop(struct event *thread);
+
+static void _err_print(const void *cookie, const char *errstr)
+{
+ std::cout << "Failed to load grpc module:" << errstr << std::endl;
+}
+
+static void static_startup(void)
+{
+ // struct frrmod_runtime module;
+ // static struct option_chain *oc;
+
+ cmd_init(1);
+
+ zlog_aux_init("NONE: ", LOG_DEBUG);
+ zprivs_preinit(&static_privs);
+ zprivs_init(&static_privs);
+
+ /* Load the server side module -- check libtool path first */
+ std::string modpath = std::string(binpath) + std::string("../../lib/.libs");
+ grpc_module = frrmod_load("grpc:50051", modpath.c_str(), 0, 0);
+ if (!grpc_module) {
+ modpath = std::string(binpath) + std::string("../../lib");
+ grpc_module = frrmod_load("grpc:50051", modpath.c_str(),
+ _err_print, 0);
+ }
+ if (!grpc_module) {
+ modpath = std::string(binpath) +
+ std::string("../../../lib/.libs");
+ grpc_module = frrmod_load("grpc:50051", modpath.c_str(),
+ _err_print, 0);
+ }
+ if (!grpc_module) {
+ modpath = std::string(binpath) + std::string("../../../lib");
+ grpc_module = frrmod_load("grpc:50051", modpath.c_str(),
+ _err_print, 0);
+ }
+ if (!grpc_module)
+ exit(1);
+
+ static_debug_init();
+
+ master = event_master_create(NULL);
+ nb_init(master, staticd_yang_modules, array_size(staticd_yang_modules),
+ false);
+
+ static_zebra_init();
+ vty_init(master, true);
+ static_vrf_init();
+ static_vty_init();
+
+ hook_register(routing_conf_event,
+ routing_control_plane_protocols_name_validate);
+
+ routing_control_plane_protocols_register_vrf_dependency();
+
+ // Add a route
+ vty = vty_new();
+ vty->type = vty::VTY_TERM;
+ vty_config_enter(vty, true, false, false);
+
+ auto ret = cmd_execute(vty, "ip route 11.0.0.0/8 Null0", NULL, 0);
+ assert(!ret);
+
+ ret = cmd_execute(vty, "end", NULL, 0);
+ assert(!ret);
+
+ nb_cli_pending_commit_check(vty);
+
+ frr_pthread_init();
+
+ // frr_config_fork();
+ hook_call(test_grpc_late_init, master);
+}
+
+static void static_shutdown(void)
+{
+ hook_call(test_grpc_fini);
+ vty_close(vty);
+ vrf_terminate();
+ vty_terminate();
+ cmd_terminate();
+ nb_terminate();
+ yang_terminate();
+ event_master_free(master);
+ master = NULL;
+}
+
+using frr::Northbound;
+using grpc::Channel;
+using grpc::ClientAsyncResponseReader;
+using grpc::ClientContext;
+using grpc::CompletionQueue;
+using grpc::Status;
+
+class NorthboundClient
+{
+ public:
+ NorthboundClient(std::shared_ptr<Channel> channel)
+ : stub_(frr::Northbound::NewStub(channel))
+ {
+ }
+
+ void Commit(uint32_t candidate_id)
+ {
+ frr::CommitRequest request;
+ frr::CommitResponse reply;
+ ClientContext context;
+ Status status;
+
+ request.set_candidate_id(candidate_id);
+
+ request.set_phase(frr::CommitRequest::ALL);
+ status = stub_->Commit(&context, request, &reply);
+ _throw_if_not_ok(status);
+#if 0
+ request.set_phase(frr::CommitRequest::VALIDATE);
+ status = stub_->Commit(&context, request, &reply);
+ _throw_if_not_ok(status);
+
+ request.set_phase(frr::CommitRequest::PREPARE);
+ status = stub_->Commit(&context, request, &reply);
+ _throw_if_not_ok(status);
+
+ request.set_phase(frr::CommitRequest::APPLY);
+ status = stub_->Commit(&context, request, &reply);
+ _throw_if_not_ok(status);
+#endif
+ }
+
+ uint32_t CreateCandidate()
+ {
+ frr::CreateCandidateRequest request;
+ frr::CreateCandidateResponse reply;
+ ClientContext context;
+ Status status;
+
+ status = stub_->CreateCandidate(&context, request, &reply);
+ _throw_if_not_ok(status);
+ return reply.candidate_id();
+ }
+
+ void DeleteCandidate(uint32_t candidate_id)
+ {
+ frr::DeleteCandidateRequest request;
+ frr::DeleteCandidateResponse reply;
+ ClientContext context;
+ Status status;
+
+ request.set_candidate_id(candidate_id);
+ status = stub_->DeleteCandidate(&context, request, &reply);
+ _throw_if_not_ok(status);
+ }
+
+ void EditCandidate(uint32_t candidate_id, const std::string &path,
+ const std::string &value)
+ {
+ frr::EditCandidateRequest request;
+ frr::EditCandidateResponse reply;
+ ClientContext context;
+
+ request.set_candidate_id(candidate_id);
+ frr::PathValue *pv = request.add_update();
+ pv->set_path(path);
+ pv->set_value(value);
+
+ Status status = stub_->EditCandidate(&context, request, &reply);
+ _throw_if_not_ok(status);
+ }
+
+ std::string Get(const std::string &path,
+ frr::GetRequest::DataType dtype, frr::Encoding enc,
+ bool with_defaults)
+ {
+ frr::GetRequest request;
+ frr::GetResponse reply;
+ ClientContext context;
+ std::ostringstream ss;
+
+ request.set_type(dtype);
+ request.set_encoding(enc);
+ request.set_with_defaults(with_defaults);
+ request.add_path(path);
+
+ auto stream = stub_->Get(&context, request);
+ while (stream->Read(&reply)) {
+ ss << reply.data().data() << std::endl;
+ }
+ auto status = stream->Finish();
+ _throw_if_not_ok(status);
+ return ss.str();
+ }
+
+ std::string GetCapabilities()
+ {
+ frr::GetCapabilitiesRequest request;
+ frr::GetCapabilitiesResponse reply;
+ ClientContext context;
+
+ Status status =
+ stub_->GetCapabilities(&context, request, &reply);
+ _throw_if_not_ok(status);
+
+ std::ostringstream ss;
+ ss << "Capabilities:" << std::endl
+ << "\tVersion: " << reply.frr_version() << std::endl
+ << "\tRollback Support: " << reply.rollback_support()
+ << std::endl
+ << "\tSupported Modules:";
+
+ for (int i = 0; i < reply.supported_modules_size(); i++) {
+ auto sm = reply.supported_modules(i);
+ ss << std::endl
+ << "\t\tName: \"" << sm.name()
+ << "\" Revision: " << sm.revision() << " Org: \""
+ << sm.organization() << "\"";
+ }
+
+ ss << std::endl << "\tSupported Encodings:";
+
+ for (int i = 0; i < reply.supported_encodings_size(); i++) {
+ auto se = reply.supported_encodings(i);
+ auto desc =
+ google::protobuf::GetEnumDescriptor<decltype(
+ se)>();
+ ss << std::endl
+ << "\t\t" << desc->FindValueByNumber(se)->name();
+ }
+
+ ss << std::endl;
+
+ return ss.str();
+ }
+
+ void LoadToCandidate(uint32_t candidate_id, bool is_replace,
+ bool is_json, const std::string &data)
+ {
+ frr::LoadToCandidateRequest request;
+ frr::LoadToCandidateResponse reply;
+ frr::DataTree *dt = new frr::DataTree;
+ ClientContext context;
+
+ request.set_candidate_id(candidate_id);
+ request.set_type(is_replace
+ ? frr::LoadToCandidateRequest::REPLACE
+ : frr::LoadToCandidateRequest::MERGE);
+ dt->set_encoding(is_json ? frr::JSON : frr::XML);
+ dt->set_data(data);
+ request.set_allocated_config(dt);
+
+ Status status =
+ stub_->LoadToCandidate(&context, request, &reply);
+ _throw_if_not_ok(status);
+ }
+
+ std::string ListTransactions()
+ {
+ frr::ListTransactionsRequest request;
+ frr::ListTransactionsResponse reply;
+ ClientContext context;
+ std::ostringstream ss;
+
+ auto stream = stub_->ListTransactions(&context, request);
+
+ while (stream->Read(&reply)) {
+ ss << "Tx ID: " << reply.id()
+ << " client: " << reply.client()
+ << " date: " << reply.date()
+ << " comment: " << reply.comment() << std::endl;
+ }
+
+ auto status = stream->Finish();
+ _throw_if_not_ok(status);
+ return ss.str();
+ }
+
+ private:
+ std::unique_ptr<frr::Northbound::Stub> stub_;
+
+ void _throw_if_not_ok(Status &status)
+ {
+ if (!status.ok())
+ throw std::runtime_error(
+ std::to_string(status.error_code()) + ": "
+ + status.error_message());
+ }
+};
+
+
+bool stop = false;
+
+int grpc_client_test_stop(struct frr_pthread *fpt, void **result)
+{
+ test_debug("client: STOP pthread");
+
+ assert(fpt->running);
+ atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
+
+ test_debug("client: joining pthread");
+ pthread_join(fpt->thread, result);
+
+ test_debug("client: joined pthread");
+ return 0;
+}
+
+int find_first_diff(const std::string &s1, const std::string &s2)
+{
+ int s1len = s1.length();
+ int s2len = s2.length();
+ int mlen = std::min(s1len, s2len);
+
+ for (int i = 0; i < mlen; i++)
+ if (s1[i] != s2[i])
+ return i;
+ return s1len == s2len ? -1 : mlen;
+}
+
+void assert_no_diff(const std::string &s1, const std::string &s2)
+{
+ int pos = find_first_diff(s1, s2);
+ if (pos == -1)
+ return;
+ std::cout << "not ok" << std::endl;
+ std::cout << "Same: " << s1.substr(0, pos) << std::endl;
+ std::cout << "Diff s1: " << s1.substr(pos) << std::endl;
+ std::cout << "Diff s2: " << s2.substr(pos) << std::endl;
+ assert(false);
+}
+
+void assert_config_same(NorthboundClient &client, const std::string &compare)
+{
+ std::string confs = client.Get("/frr-routing:routing",
+ frr::GetRequest::ALL, frr::JSON, true);
+ assert_no_diff(confs, compare);
+ std::cout << "ok" << std::endl;
+}
+
+void grpc_client_run_test(void)
+{
+ NorthboundClient client(grpc::CreateChannel(
+ "localhost:50051", grpc::InsecureChannelCredentials()));
+
+ std::string reply = client.GetCapabilities();
+
+ uint32_t cid;
+ cid = client.CreateCandidate();
+ std::cout << "CreateCandidate -> " << cid << std::endl;
+ assert(cid == 1);
+ client.DeleteCandidate(cid);
+ std::cout << "DeleteCandidate(" << cid << ")" << std::endl;
+ cid = client.CreateCandidate();
+ assert(cid == 2);
+ std::cout << "CreateCandidate -> " << cid << std::endl;
+
+ /*
+ * Get initial configuration
+ */
+ std::cout << "Comparing initial config...";
+ assert_config_same(client, json_expect1);
+
+ /*
+ * Add config using EditCandidate
+ */
+
+ char xpath_buf[1024];
+ strlcpy(xpath_buf,
+ "/frr-routing:routing/control-plane-protocols/"
+ "control-plane-protocol[type='frr-staticd:staticd']"
+ "[name='staticd'][vrf='default']/frr-staticd:staticd/route-list",
+ sizeof(xpath_buf));
+ int slen = strlen(xpath_buf);
+ for (int i = 0; i < 4; i++) {
+ snprintf(xpath_buf + slen, sizeof(xpath_buf) - slen,
+ "[prefix='13.0.%d.0/24']"
+ "[afi-safi='frr-routing:ipv4-unicast']/"
+ "path-list[table-id='0'][distance='1']/"
+ "frr-nexthops/nexthop[nh-type='blackhole']"
+ "[vrf='default'][gateway=''][interface='(null)']",
+ i);
+ client.EditCandidate(cid, xpath_buf, "");
+ }
+ client.Commit(cid);
+ std::cout << "Comparing EditCandidate config...";
+ assert_config_same(client, json_expect2);
+
+ client.DeleteCandidate(cid);
+ std::cout << "DeleteCandidate(" << cid << ")" << std::endl;
+
+ /*
+ * Add config using LoadToCandidate
+ */
+
+ cid = client.CreateCandidate();
+ std::cout << "CreateCandidate -> " << cid << std::endl;
+
+ client.LoadToCandidate(cid, false, true, json_loadconf1);
+ client.Commit(cid);
+
+ std::cout << "Comparing LoadToCandidate config...";
+ assert_config_same(client, json_expect3);
+
+ client.DeleteCandidate(cid);
+ std::cout << "DeleteCandidate(" << cid << ")" << std::endl;
+
+ std::string ltxreply = client.ListTransactions();
+ // std::cout << "client: pthread received: " << ltxreply << std::endl;
+}
+
+void *grpc_client_test_start(void *arg)
+{
+ struct frr_pthread *fpt = (struct frr_pthread *)arg;
+ fpt->master->owner = pthread_self();
+ frr_pthread_set_name(fpt);
+ frr_pthread_notify_running(fpt);
+
+ try {
+ grpc_client_run_test();
+ std::cout << "TEST PASSED" << std::endl;
+ } catch (std::exception &e) {
+ std::cout << "Exception in test: " << e.what() << std::endl;
+ }
+
+ // Signal FRR event loop to stop
+ test_debug("client: pthread: adding event to stop us");
+ event_add_event(master, grpc_thread_stop, NULL, 0, NULL);
+
+ test_debug("client: pthread: DONE (returning)");
+
+ return NULL;
+}
+
+static void grpc_thread_start(struct event *thread)
+{
+ struct frr_pthread_attr client = {
+ .start = grpc_client_test_start,
+ .stop = grpc_client_test_stop,
+ };
+
+ auto pth = frr_pthread_new(&client, "GRPC Client thread", "grpc");
+ frr_pthread_run(pth, NULL);
+ frr_pthread_wait_running(pth);
+}
+
+static void grpc_thread_stop(struct event *thread)
+{
+ std::cout << __func__ << ": frr_pthread_stop_all" << std::endl;
+ frr_pthread_stop_all();
+ std::cout << __func__ << ": static_shutdown" << std::endl;
+ static_shutdown();
+ std::cout << __func__ << ": exit cleanly" << std::endl;
+ exit(0);
+}
+
+/*
+ * return abs path to this binary with trailing `/`. Does not parse path
+ * environment to find in path, which should not matter for unit testing.
+ */
+static int get_binpath(const char *argv0, char cwd[2 * MAXPATHLEN + 1])
+{
+ const char *rch;
+ if (argv0[0] == '/') {
+ *cwd = 0;
+ rch = strrchr(argv0, '/');
+ strlcpy(cwd, argv0, MIN(rch - argv0 + 2, 2 * MAXPATHLEN + 1));
+ return 0;
+ }
+ if (!(rch = strrchr(argv0, '/'))) {
+ /* Does not handle using PATH, shouldn't matter for test */
+ errno = EINVAL;
+ return -1;
+ }
+ if (!getcwd(cwd, MAXPATHLEN))
+ return -1;
+ int len = strlen(cwd);
+ cwd[len++] = '/';
+ strlcpy(cwd + len, argv0, MIN(rch - argv0 + 2, 2 * MAXPATHLEN + 1));
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ assert(argc >= 1);
+ if (get_binpath(argv[0], binpath) < 0)
+ exit(1);
+
+ static_startup();
+
+ event_add_event(master, grpc_thread_start, NULL, 0, NULL);
+
+ /* Event Loop */
+ struct event thread;
+ while (event_fetch(master, &thread))
+ event_call(&thread);
+ return 0;
+}
+
+// clang-format off
+
+const char *json_expect1 = R"NONCE({
+ "frr-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "type": "frr-staticd:staticd",
+ "name": "staticd",
+ "vrf": "default",
+ "frr-staticd:staticd": {
+ "route-list": [
+ {
+ "prefix": "11.0.0.0/8",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "frr-vrf:lib": {
+ "vrf": [
+ {
+ "name": "default",
+ "state": {
+ "active": false
+ }
+ }
+ ]
+ }
+}
+
+)NONCE";
+
+const char *json_loadconf1 = R"NONCE(
+{
+ "frr-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "type": "frr-staticd:staticd",
+ "name": "staticd",
+ "vrf": "default",
+ "frr-staticd:staticd": {
+ "route-list": [
+ {
+ "prefix": "10.0.0.0/13",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "frr-vrf:lib": {
+ "vrf": [
+ {
+ "name": "default"
+ }
+ ]
+ }
+})NONCE";
+
+const char *json_expect2 = R"NONCE({
+ "frr-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "type": "frr-staticd:staticd",
+ "name": "staticd",
+ "vrf": "default",
+ "frr-staticd:staticd": {
+ "route-list": [
+ {
+ "prefix": "11.0.0.0/8",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.0.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.1.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.2.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.3.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "frr-vrf:lib": {
+ "vrf": [
+ {
+ "name": "default",
+ "state": {
+ "active": false
+ }
+ }
+ ]
+ }
+}
+
+)NONCE";
+
+const char *json_expect3 = R"NONCE({
+ "frr-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "type": "frr-staticd:staticd",
+ "name": "staticd",
+ "vrf": "default",
+ "frr-staticd:staticd": {
+ "route-list": [
+ {
+ "prefix": "11.0.0.0/8",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.0.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.1.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.2.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.3.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "10.0.0.0/13",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "frr-vrf:lib": {
+ "vrf": [
+ {
+ "name": "default",
+ "state": {
+ "active": false
+ }
+ }
+ ]
+ }
+}
+
+)NONCE";
diff --git a/tests/lib/test_grpc.py b/tests/lib/test_grpc.py
new file mode 100644
index 0000000..7f722de
--- /dev/null
+++ b/tests/lib/test_grpc.py
@@ -0,0 +1,33 @@
+import inspect
+import os
+import subprocess
+
+import frrtest
+import pytest
+
+
+class TestGRPC(object):
+ program = "./test_grpc"
+
+ @pytest.mark.skipif(
+ 'S["GRPC_TRUE"]=""\n' not in open("../config.status").readlines(),
+ reason="GRPC not enabled",
+ )
+ @pytest.mark.skipif(
+ not os.path.isdir("/usr/share/yang"),
+ reason="YANG models aren't installed in /usr/share/yang",
+ )
+ def test_exits_cleanly(self):
+ basedir = os.path.dirname(inspect.getsourcefile(type(self)))
+ program = os.path.join(basedir, self.program)
+ proc = subprocess.Popen(
+ [frrtest.binpath(program)],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ output, _ = proc.communicate()
+ self.exitcode = proc.wait()
+ if self.exitcode != 0:
+ print("OUTPUT:\n" + output.decode("ascii"))
+ raise frrtest.TestExitNonzero(self)
diff --git a/tests/lib/test_heavy.c b/tests/lib/test_heavy.c
new file mode 100644
index 0000000..1e56940
--- /dev/null
+++ b/tests/lib/test_heavy.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+/* This programme shows the effects of 'heavy' long-running functions
+ * on the cooperative threading model.
+ *
+ * Run it with a config file containing 'password whatever', telnet to it
+ * (it defaults to port 4000) and enter the 'clear foo string' command.
+ * then type whatever and observe that the vty interface is unresponsive
+ * for quite a period of time, due to the clear_something command
+ * taking a very long time to complete.
+ */
+#include <zebra.h>
+
+#include "frrevent.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include <math.h>
+
+#include "tests.h"
+
+enum { ITERS_FIRST = 0,
+ ITERS_ERR = 100,
+ ITERS_LATER = 400,
+ ITERS_PRINT = 10,
+ ITERS_MAX = 1000,
+};
+
+static void slow_func(struct vty *vty, const char *str, const int i)
+{
+ double x = 1;
+ int j;
+
+ for (j = 0; j < 300; j++)
+ x += sin(x) * j;
+
+ if ((i % ITERS_LATER) == 0)
+ printf("%s: %d, temporary error, save this somehow and do it later..\n",
+ __func__, i);
+
+ if ((i % ITERS_ERR) == 0)
+ printf("%s: hard error\n", __func__);
+
+ if ((i % ITERS_PRINT) == 0)
+ printf("%s did %d, x = %g\n", str, i, x);
+}
+
+static void clear_something(struct vty *vty, const char *str)
+{
+ int i;
+
+ /* this could be like iterating through 150k of route_table
+ * or worse, iterating through a list of peers, to bgp_stop them with
+ * each having 150k route tables to process...
+ */
+ for (i = ITERS_FIRST; i < ITERS_MAX; i++)
+ slow_func(vty, str, i);
+}
+
+DEFUN (clear_foo,
+ clear_foo_cmd,
+ "clear foo LINE...",
+ "clear command\n"
+ "arbitrary string\n")
+{
+ char *str;
+ if (!argc) {
+ vty_out(vty, "%% string argument required\n");
+ return CMD_WARNING;
+ }
+
+ str = argv_concat(argv, argc, 0);
+
+ clear_something(vty, str);
+ XFREE(MTYPE_TMP, str);
+ return CMD_SUCCESS;
+}
+
+static void slow_vty_init(void)
+{
+ install_element(VIEW_NODE, &clear_foo_cmd);
+}
+
+void test_init(void)
+{
+ slow_vty_init();
+}
diff --git a/tests/lib/test_heavy_thread.c b/tests/lib/test_heavy_thread.c
new file mode 100644
index 0000000..4fb9ebf
--- /dev/null
+++ b/tests/lib/test_heavy_thread.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+/* This programme shows the effects of 'heavy' long-running functions
+ * on the cooperative threading model, as demonstrated by heavy.c, and how
+ * they can be mitigated using a background thread.
+ *
+ * Run it with a config file containing 'password whatever', telnet to it
+ * (it defaults to port 4000) and enter the 'clear foo string' command.
+ * then type whatever and observe that, unlike heavy.c, the vty interface
+ * remains responsive.
+ */
+#include <zebra.h>
+#include <math.h>
+
+#include "frrevent.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "log.h"
+
+#include "tests.h"
+
+extern struct event_loop *master;
+
+enum { ITERS_FIRST = 0,
+ ITERS_ERR = 100,
+ ITERS_LATER = 400,
+ ITERS_PRINT = 10,
+ ITERS_MAX = 1000,
+};
+
+struct work_state {
+ struct vty *vty;
+ char *str;
+ int i;
+};
+
+static void slow_func(struct vty *vty, const char *str, const int i)
+{
+ double x = 1;
+ int j;
+
+ for (j = 0; j < 300; j++)
+ x += sin(x) * j;
+
+ if ((i % ITERS_LATER) == 0)
+ printf("%s: %d, temporary error, save this somehow and do it later..\n",
+ __func__, i);
+
+ if ((i % ITERS_ERR) == 0)
+ printf("%s: hard error\n", __func__);
+
+ if ((i % ITERS_PRINT) == 0)
+ printf("%s did %d, x = %g\n", str, i, x);
+}
+
+static void clear_something(struct event *thread)
+{
+ struct work_state *ws = EVENT_ARG(thread);
+
+ /* this could be like iterating through 150k of route_table
+ * or worse, iterating through a list of peers, to bgp_stop them with
+ * each having 150k route tables to process...
+ */
+ while (ws->i < ITERS_MAX) {
+ slow_func(ws->vty, ws->str, ws->i);
+ ws->i++;
+ if (event_should_yield(thread)) {
+ event_add_timer_msec(master, clear_something, ws, 0,
+ NULL);
+ return;
+ }
+ }
+
+ /* All done! */
+ XFREE(MTYPE_TMP, ws->str);
+ XFREE(MTYPE_TMP, ws);
+}
+
+DEFUN (clear_foo,
+ clear_foo_cmd,
+ "clear foo LINE...",
+ "clear command\n"
+ "arbitrary string\n")
+{
+ char *str;
+ struct work_state *ws;
+
+ if (!argc) {
+ vty_out(vty, "%% string argument required\n");
+ return CMD_WARNING;
+ }
+
+ str = argv_concat(argv, argc, 0);
+
+ ws = XMALLOC(MTYPE_TMP, sizeof(*ws));
+
+ ws->str = XSTRDUP(MTYPE_TMP, str);
+
+ ws->vty = vty;
+ ws->i = ITERS_FIRST;
+
+ event_add_timer_msec(master, clear_something, ws, 0, NULL);
+
+ return CMD_SUCCESS;
+}
+
+void test_init(void)
+{
+ install_element(VIEW_NODE, &clear_foo_cmd);
+}
diff --git a/tests/lib/test_heavy_wq.c b/tests/lib/test_heavy_wq.c
new file mode 100644
index 0000000..225573a
--- /dev/null
+++ b/tests/lib/test_heavy_wq.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+/* This programme shows the effects of 'heavy' long-running functions
+ * on the cooperative threading model.
+ *
+ * Run it with a config file containing 'password whatever', telnet to it
+ * (it defaults to port 4000) and enter the 'clear foo string' command.
+ * then type whatever and observe that the vty interface is unresponsive
+ * for quite a period of time, due to the clear_something command
+ * taking a very long time to complete.
+ */
+#include <zebra.h>
+
+#include "frrevent.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "log.h"
+#include "workqueue.h"
+#include <math.h>
+
+#include "tests.h"
+
+DEFINE_MGROUP(TEST_HEAVYWQ, "heavy-wq test");
+DEFINE_MTYPE_STATIC(TEST_HEAVYWQ, WQ_NODE, "heavy_wq_node");
+DEFINE_MTYPE_STATIC(TEST_HEAVYWQ, WQ_NODE_STR, "heavy_wq_node->str");
+
+extern struct event_loop *master;
+static struct work_queue *heavy_wq;
+
+struct heavy_wq_node {
+ char *str;
+ int i;
+};
+
+enum { ITERS_FIRST = 0,
+ ITERS_ERR = 100,
+ ITERS_LATER = 400,
+ ITERS_PRINT = 10,
+ ITERS_MAX = 1000,
+};
+
+static void heavy_wq_add(struct vty *vty, const char *str, int i)
+{
+ struct heavy_wq_node *hn;
+
+ hn = XCALLOC(MTYPE_WQ_NODE, sizeof(struct heavy_wq_node));
+
+ hn->i = i;
+ hn->str = XSTRDUP(MTYPE_WQ_NODE_STR, str);
+
+ work_queue_add(heavy_wq, hn);
+
+ return;
+}
+
+static void slow_func_del(struct work_queue *wq, void *data)
+{
+ struct heavy_wq_node *hn = data;
+ assert(hn && hn->str);
+ printf("%s: %s\n", __func__, hn->str);
+ XFREE(MTYPE_WQ_NODE_STR, hn->str);
+ XFREE(MTYPE_WQ_NODE, hn);
+}
+
+static wq_item_status slow_func(struct work_queue *wq, void *data)
+{
+ struct heavy_wq_node *hn = data;
+ double x = 1;
+ int j;
+
+ assert(hn && hn->str);
+
+ for (j = 0; j < 300; j++)
+ x += sin(x) * j;
+
+ if ((hn->i % ITERS_LATER) == 0)
+ return WQ_RETRY_LATER;
+
+ if ((hn->i % ITERS_ERR) == 0)
+ return WQ_RETRY_NOW;
+
+ if ((hn->i % ITERS_PRINT) == 0)
+ printf("%s did %d, x = %g\n", hn->str, hn->i, x);
+
+ return WQ_SUCCESS;
+}
+
+static void clear_something(struct vty *vty, const char *str)
+{
+ int i;
+
+ /* this could be like iterating through 150k of route_table
+ * or worse, iterating through a list of peers, to bgp_stop them with
+ * each having 150k route tables to process...
+ */
+ for (i = ITERS_FIRST; i < ITERS_MAX; i++)
+ heavy_wq_add(vty, str, i);
+}
+
+DEFUN (clear_foo,
+ clear_foo_cmd,
+ "clear foo LINE...",
+ "clear command\n"
+ "arbitrary string\n")
+{
+ char *str;
+ if (!argc) {
+ vty_out(vty, "%% string argument required\n");
+ return CMD_WARNING;
+ }
+
+ str = argv_concat(argv, argc, 0);
+
+ clear_something(vty, str);
+ XFREE(MTYPE_TMP, str);
+ return CMD_SUCCESS;
+}
+
+static int heavy_wq_init(void)
+{
+ heavy_wq = work_queue_new(master, "heavy_work_queue");
+
+ heavy_wq->spec.workfunc = &slow_func;
+ heavy_wq->spec.del_item_data = &slow_func_del;
+ heavy_wq->spec.max_retries = 3;
+ heavy_wq->spec.hold = 1000;
+
+ return 0;
+}
+
+void test_init(void)
+{
+ install_element(VIEW_NODE, &clear_foo_cmd);
+ heavy_wq_init();
+}
diff --git a/tests/lib/test_idalloc.c b/tests/lib/test_idalloc.c
new file mode 100644
index 0000000..ce1582b
--- /dev/null
+++ b/tests/lib/test_idalloc.c
@@ -0,0 +1,197 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "id_alloc.h"
+
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#define IDS_PER_PAGE (1<<(IDALLOC_OFFSET_BITS + IDALLOC_WORD_BITS))
+char allocated_markers[IDS_PER_PAGE*3];
+
+int main(int argc, char **argv)
+{
+ int i, val;
+ uint32_t pg;
+ struct id_alloc *a;
+
+ /* 1. Rattle test, shake it a little and make sure it doesn't make any
+ * noise :)
+ */
+ a = idalloc_new("Rattle test");
+ for (i = 0; i < 1000000; i++)
+ assert(idalloc_allocate(a) != 0);
+
+ idalloc_destroy(a);
+
+ /* 2. Reserve a few low IDs, make sure they are skipped by normal
+ * allocation.
+ */
+ a = idalloc_new("Low Reservations");
+ assert(idalloc_reserve(a, 1) == 1);
+ assert(idalloc_reserve(a, 3) == 3);
+ assert(idalloc_reserve(a, 5) == 5);
+ for (i = 0; i < 100; i++) {
+ val = idalloc_allocate(a);
+ assert(val != 1 && val != 3 && val != 5);
+ }
+ idalloc_destroy(a);
+
+ /* 3. Single page testing. Check that IDs are kept unique, and all IDs
+ * in the existing page are allocated before a new page is added.
+ */
+ memset(allocated_markers, 0, sizeof(allocated_markers));
+ allocated_markers[IDALLOC_INVALID] = 1;
+
+ a = idalloc_new("Single Page");
+
+ /* reserve the rest of the first page */
+ for (i = 0; i < IDS_PER_PAGE - 1; i++) {
+ val = idalloc_allocate(a);
+ assert(val < IDS_PER_PAGE);
+ assert(allocated_markers[val] == 0);
+ assert(a->capacity == IDS_PER_PAGE);
+ allocated_markers[val] = 1;
+ }
+ /* Check that the count is right */
+ assert(a->allocated == IDS_PER_PAGE);
+
+ /* Free some IDs out of the middle. */
+ idalloc_free(a, 300);
+ allocated_markers[300] = 0;
+ idalloc_free(a, 400);
+ allocated_markers[400] = 0;
+ idalloc_free(a, 500);
+ allocated_markers[500] = 0;
+
+ assert(a->allocated == IDS_PER_PAGE-3);
+
+ /* Allocate the three IDs back and make sure they are pulled from the
+ * set just freed
+ */
+ for (i = 0; i < 3; i++) {
+ val = idalloc_allocate(a);
+ assert(val < IDS_PER_PAGE);
+ assert(allocated_markers[val] == 0);
+ assert(a->capacity == IDS_PER_PAGE);
+ allocated_markers[val] = 1;
+ }
+ idalloc_destroy(a);
+
+ /* 4. Multi-page testing. */
+ memset(allocated_markers, 0, sizeof(allocated_markers));
+ allocated_markers[IDALLOC_INVALID] = 1;
+
+ a = idalloc_new("Multi-page");
+
+ /* reserve the rest of the first page and all of the second and third */
+ for (i = 0; i < 3 * IDS_PER_PAGE - 1; i++) {
+ val = idalloc_allocate(a);
+ assert(val < 3*IDS_PER_PAGE);
+ assert(allocated_markers[val] == 0);
+ allocated_markers[val] = 1;
+ }
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE);
+
+ /* Free two IDs from each page. */
+ for (i = 0; i < 3; i++) {
+ idalloc_free(a, 7 + i*IDS_PER_PAGE);
+ allocated_markers[7 + i*IDS_PER_PAGE] = 0;
+
+ idalloc_free(a, 4 + i*IDS_PER_PAGE);
+ allocated_markers[4 + i*IDS_PER_PAGE] = 0;
+ }
+
+ assert(a->allocated == 3*IDS_PER_PAGE - 6);
+
+ /* Allocate the six IDs back and make sure they are pulled from the set
+ * just freed.
+ */
+ for (i = 0; i < 6; i++) {
+ val = idalloc_allocate(a);
+ assert(val < 3*IDS_PER_PAGE);
+ assert(allocated_markers[val] == 0);
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ allocated_markers[val] = 1;
+ }
+
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE);
+
+ /* Walk each allocated ID. Free it, then re-allocate it back. */
+ for (i = 1; i < 3 * IDS_PER_PAGE - 1; i++) {
+ idalloc_free(a, i);
+ val = idalloc_allocate(a);
+ assert(val == i);
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE);
+ }
+ idalloc_destroy(a);
+
+ /* 5. Weird Reservations
+ * idalloc_reserve exists primarily to black out low numbered IDs that
+ * are reserved for special cases. However, we will test it for more
+ * complex use cases to avoid unpleasant surprises.
+ */
+
+ memset(allocated_markers, 0, sizeof(allocated_markers));
+ allocated_markers[IDALLOC_INVALID] = 1;
+
+ a = idalloc_new("Weird Reservations");
+
+ /* Start with 3 pages fully allocated. */
+ for (i = 0; i < 3 * IDS_PER_PAGE - 1; i++) {
+ val = idalloc_allocate(a);
+ assert(val < 3*IDS_PER_PAGE);
+ assert(allocated_markers[val] == 0);
+ allocated_markers[val] = 1;
+ }
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE);
+
+ /* Free a bit out of each of the three pages. Then reserve one of the
+ * three freed IDs. Finally, allocate the other two freed IDs. Do this
+ * each of three ways. (Reserve out of the first, seconds then third
+ * page.)
+ * The intent here is to exercise the rare cases on reserve_bit's
+ * linked-list removal in the case that it is not removing the first
+ * page with a free bit in its list of pages with free bits.
+ */
+
+ for (pg = 0; pg < 3; pg++) {
+ /* free a bit out of each of the three pages */
+ for (i = 0; i < 3; i++) {
+ idalloc_free(a, i*IDS_PER_PAGE + 17);
+ allocated_markers[i*IDS_PER_PAGE + 17] = 0;
+ }
+
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE-3);
+
+ /* Reserve one of the freed IDs */
+ assert(idalloc_reserve(a, pg*IDS_PER_PAGE + 17) ==
+ pg*IDS_PER_PAGE + 17);
+ allocated_markers[pg*IDS_PER_PAGE + 17] = 1;
+
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE-2);
+
+ /* Allocate the other two back */
+ for (i = 0; i < 2; i++) {
+ val = idalloc_allocate(a);
+ assert(val < 3*IDS_PER_PAGE);
+ assert(allocated_markers[val] == 0);
+ allocated_markers[val] = 1;
+ }
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE);
+ }
+ idalloc_destroy(a);
+
+ puts("ID Allocator test successful.\n");
+ return 0;
+}
diff --git a/tests/lib/test_idalloc.py b/tests/lib/test_idalloc.py
new file mode 100644
index 0000000..e2186dc
--- /dev/null
+++ b/tests/lib/test_idalloc.py
@@ -0,0 +1,8 @@
+import frrtest
+
+
+class TestIDAlloc(frrtest.TestMultiOut):
+ program = "./test_idalloc"
+
+
+TestIDAlloc.onesimple("ID Allocator test successful.")
diff --git a/tests/lib/test_memory.c b/tests/lib/test_memory.c
new file mode 100644
index 0000000..aba4c35
--- /dev/null
+++ b/tests/lib/test_memory.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+#include <zebra.h>
+#include <memory.h>
+
+DEFINE_MGROUP(TEST_MEMORY, "memory test");
+DEFINE_MTYPE_STATIC(TEST_MEMORY, TEST, "generic test mtype");
+
+/* Memory torture tests
+ *
+ * Tests below are generic but comments are focused on interaction with
+ * Paul's proposed memory 'quick' cache, which may never be included in
+ * CVS
+ */
+
+struct event_loop *master;
+
+#if 0 /* set to 1 to use system alloc directly */
+#undef XMALLOC
+#undef XCALLOC
+#undef XREALLOC
+#undef XFREE
+#define XMALLOC(T,S) malloc((S))
+#define XCALLOC(T,S) calloc(1, (S))
+#define XREALLOC(T,P,S) realloc((P),(S))
+#define XFREE(T,P) free((P))
+#endif
+
+#define TIMES 10
+
+int main(int argc, char **argv)
+{
+ void *a[10];
+ int i;
+
+ printf("malloc x, malloc x, free, malloc x, free free\n\n");
+ /* simple case, test cache */
+ for (i = 0; i < TIMES; i++) {
+ a[0] = XMALLOC(MTYPE_TEST, 1024);
+ memset(a[0], 1, 1024);
+ a[1] = XMALLOC(MTYPE_TEST, 1024);
+ memset(a[1], 1, 1024);
+ XFREE(MTYPE_TEST, a[0]); /* should go to cache */
+ a[0] = XMALLOC(MTYPE_TEST,
+ 1024); /* should be satisfied from cache */
+ XFREE(MTYPE_TEST, a[0]);
+ XFREE(MTYPE_TEST, a[1]);
+ }
+
+ printf("malloc x, malloc y, free x, malloc y, free free\n\n");
+ /* cache should go invalid, valid, invalid, etc.. */
+ for (i = 0; i < TIMES; i++) {
+ a[0] = XMALLOC(MTYPE_TEST, 512);
+ memset(a[0], 1, 512);
+ a[1] = XMALLOC(MTYPE_TEST, 1024); /* invalidate cache */
+ memset(a[1], 1, 1024);
+ XFREE(MTYPE_TEST, a[0]);
+ a[0] = XMALLOC(MTYPE_TEST, 1024);
+ XFREE(MTYPE_TEST, a[0]);
+ XFREE(MTYPE_TEST, a[1]);
+ /* cache should become valid again on next request */
+ }
+
+ printf("calloc\n\n");
+ /* test calloc */
+ for (i = 0; i < TIMES; i++) {
+ a[0] = XCALLOC(MTYPE_TEST, 1024);
+ memset(a[0], 1, 1024);
+ a[1] = XCALLOC(MTYPE_TEST, 512); /* invalidate cache */
+ memset(a[1], 1, 512);
+ XFREE(MTYPE_TEST, a[1]);
+ XFREE(MTYPE_TEST, a[0]);
+ /* alloc == 0, cache can become valid again on next request */
+ }
+
+ printf("calloc and realloc\n\n");
+ /* check calloc + realloc */
+ for (i = 0; i < TIMES; i++) {
+ printf("calloc a0 1024\n");
+ a[0] = XCALLOC(MTYPE_TEST, 1024);
+ memset(a[0], 1, 1024 / 2);
+
+ printf("calloc 1 1024\n");
+ a[1] = XCALLOC(MTYPE_TEST, 1024);
+ memset(a[1], 1, 1024 / 2);
+
+ printf("realloc 0 1024\n");
+ a[3] = XREALLOC(MTYPE_TEST, a[0], 2048); /* invalidate cache */
+ if (a[3] != NULL)
+ a[0] = a[3];
+ memset(a[0], 1, 1024);
+
+ printf("calloc 2 512\n");
+ a[2] = XCALLOC(MTYPE_TEST, 512);
+ memset(a[2], 1, 512);
+
+ printf("free 1 0 2\n");
+ XFREE(MTYPE_TEST, a[1]);
+ XFREE(MTYPE_TEST, a[0]);
+ XFREE(MTYPE_TEST, a[2]);
+ /* alloc == 0, cache valid next request */
+ }
+ return 0;
+}
diff --git a/tests/lib/test_nexthop.c b/tests/lib/test_nexthop.c
new file mode 100644
index 0000000..84732d6
--- /dev/null
+++ b/tests/lib/test_nexthop.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Nexthop module test.
+ *
+ * Copyright (C) 2021 by Volta Networks, Inc.
+ */
+
+#include <zebra.h>
+#include <nexthop.h>
+
+static bool verbose;
+
+static void test_run_first(void)
+{
+ int ret, i;
+ struct nexthop *nh1, *nh2;
+ struct in_addr addr;
+ struct in6_addr addr6;
+ mpls_label_t labels[MPLS_MAX_LABELS];
+
+ /* Test comparison apis */
+
+ /* ifindex comparisons */
+ nh1 = nexthop_from_ifindex(11, 0);
+ nh2 = nexthop_from_ifindex(12, 0);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret < 0);
+
+ nexthop_free(nh1);
+ nh1 = nexthop_from_ifindex(12, 0);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret == 0);
+
+ nexthop_free(nh1);
+ nexthop_free(nh2);
+
+ /* ipv4, vrf */
+ addr.s_addr = 0x04030201;
+ nh1 = nexthop_from_ipv4(&addr, NULL, 0);
+ nh2 = nexthop_from_ipv4(&addr, NULL, 111);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret != 0);
+
+ nexthop_free(nh2);
+
+ addr.s_addr = 0x04030202;
+ nh2 = nexthop_from_ipv4(&addr, NULL, 0);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret != 0);
+
+ nexthop_free(nh2);
+
+ addr.s_addr = 0x04030201;
+ nh2 = nexthop_from_ipv4(&addr, NULL, 0);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret == 0);
+
+ /* Weight */
+ nh2->weight = 20;
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret != 0);
+
+ nexthop_free(nh1);
+ nexthop_free(nh2);
+
+ /* ipv6 */
+ memset(addr6.s6_addr, 0, sizeof(addr6.s6_addr));
+ nh1 = nexthop_from_ipv6(&addr6, 0);
+ nh2 = nexthop_from_ipv6(&addr6, 0);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret == 0);
+
+ nexthop_free(nh2);
+
+ nh2 = nexthop_from_ipv6(&addr6, 1);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret != 0);
+
+ nexthop_free(nh2);
+
+ addr6.s6_addr[14] = 1;
+ addr6.s6_addr[15] = 1;
+ nh2 = nexthop_from_ipv6(&addr6, 0);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret != 0);
+
+ nexthop_free(nh1);
+ nexthop_free(nh2);
+
+ /* Blackhole */
+ nh1 = nexthop_from_blackhole(BLACKHOLE_REJECT, 0);
+ nh2 = nexthop_from_blackhole(BLACKHOLE_REJECT, 0);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret == 0);
+
+ nexthop_free(nh2);
+
+ nh2 = nexthop_from_blackhole(BLACKHOLE_NULL, 0);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret != 0);
+
+ /* Labels */
+ addr.s_addr = 0x04030201;
+ nh1 = nexthop_from_ipv4(&addr, NULL, 0);
+ nh2 = nexthop_from_ipv4(&addr, NULL, 0);
+
+ memset(labels, 0, sizeof(labels));
+ labels[0] = 111;
+ labels[1] = 222;
+
+ nexthop_add_labels(nh1, ZEBRA_LSP_STATIC, 2, labels);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret != 0);
+
+ nexthop_add_labels(nh2, ZEBRA_LSP_STATIC, 2, labels);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret == 0);
+
+ nexthop_free(nh2);
+
+ /* LSP type isn't included */
+ nh2 = nexthop_from_ipv4(&addr, NULL, 0);
+ nexthop_add_labels(nh2, ZEBRA_LSP_LDP, 2, labels);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret == 0);
+
+ nexthop_free(nh2);
+
+ labels[2] = 333;
+ nh2 = nexthop_from_ipv4(&addr, NULL, 0);
+ nexthop_add_labels(nh2, ZEBRA_LSP_LDP, 3, labels);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret != 0);
+
+ nexthop_free(nh1);
+ nexthop_free(nh2);
+
+ nh1 = nexthop_from_ipv4(&addr, NULL, 0);
+ nh2 = nexthop_from_ipv4(&addr, NULL, 0);
+
+ for (i = 0; i < MPLS_MAX_LABELS; i++)
+ labels[i] = 111 * (i + 1);
+
+ nexthop_add_labels(nh1, ZEBRA_LSP_LDP, MPLS_MAX_LABELS, labels);
+ nexthop_add_labels(nh2, ZEBRA_LSP_LDP, MPLS_MAX_LABELS, labels);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret == 0);
+
+ nexthop_free(nh2);
+
+ /* Test very last label in stack */
+ labels[15] = 999;
+ nh2 = nexthop_from_ipv4(&addr, NULL, 0);
+ nexthop_add_labels(nh2, ZEBRA_LSP_LDP, MPLS_MAX_LABELS, labels);
+
+ ret = nexthop_cmp_basic(nh1, nh2);
+ assert(ret != 0);
+
+ /* End */
+ nexthop_free(nh1);
+ nexthop_free(nh2);
+}
+
+int main(int argc, char **argv)
+{
+ if (argc >= 2 && !strcmp("-v", argv[1]))
+ verbose = true;
+ test_run_first();
+ printf("Simple test passed.\n");
+}
diff --git a/tests/lib/test_nexthop.py b/tests/lib/test_nexthop.py
new file mode 100644
index 0000000..81bfa43
--- /dev/null
+++ b/tests/lib/test_nexthop.py
@@ -0,0 +1,8 @@
+import frrtest
+
+
+class TestNexthopIter(frrtest.TestMultiOut):
+ program = "./test_nexthop"
+
+
+TestNexthopIter.onesimple("Simple test passed.")
diff --git a/tests/lib/test_nexthop_iter.c b/tests/lib/test_nexthop_iter.c
new file mode 100644
index 0000000..33ff116
--- /dev/null
+++ b/tests/lib/test_nexthop_iter.c
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Recursive Nexthop Iterator test.
+ * This tests the ALL_NEXTHOPS macro.
+ *
+ * Copyright (C) 2012 by Open Source Routing.
+ * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of Quagga
+ */
+
+#include <zebra.h>
+#include "zebra/rib.h"
+#include "prng.h"
+
+struct event_loop *master;
+static int verbose;
+
+static void str_append(char **buf, const char *repr)
+{
+ if (*buf) {
+ size_t new_size = strlen(*buf) + strlen(repr) + 1;
+ *buf = realloc(*buf, new_size);
+ assert(*buf);
+ (void)strlcat(*buf, repr, new_size);
+ } else {
+ *buf = strdup(repr);
+ assert(*buf);
+ }
+}
+
+PRINTFRR(2, 3)
+static void str_appendf(char **buf, const char *format, ...)
+{
+ va_list ap;
+ int rv;
+ char *pbuf;
+
+ va_start(ap, format);
+ rv = vasprintf(&pbuf, format, ap);
+ va_end(ap);
+ assert(rv >= 0);
+
+ str_append(buf, pbuf);
+ free(pbuf);
+}
+
+/* This structure contains a nexthop chain
+ * and its expected representation */
+struct nexthop_chain {
+ /* Head of the chain */
+ struct nexthop_group head;
+ /* Last nexthop in top chain */
+ struct nexthop *current_top;
+ /* Last nexthop in current recursive chain */
+ struct nexthop *current_recursive;
+ /* Expected string representation. */
+ char *repr;
+};
+
+static struct nexthop_chain *nexthop_chain_new(void)
+{
+ struct nexthop_chain *rv;
+
+ rv = calloc(sizeof(*rv), 1);
+ assert(rv);
+ return rv;
+}
+
+static void nexthop_chain_add_top(struct nexthop_chain *nc)
+{
+ struct nexthop *nh;
+
+ nh = calloc(sizeof(*nh), 1);
+ assert(nh);
+
+ if (nc->head.nexthop) {
+ nc->current_top->next = nh;
+ nh->prev = nc->current_top;
+ nc->current_top = nh;
+ } else {
+ nc->head.nexthop = nc->current_top = nh;
+ }
+ nc->current_recursive = NULL;
+ str_appendf(&nc->repr, "%p\n", nh);
+}
+
+static void add_string_representation(char **repr, struct nexthop *nh)
+{
+ struct nexthop *parent;
+
+ /* add indentations first */
+ parent = nh->rparent;
+ while (parent) {
+ str_appendf(repr, " ");
+ parent = parent->rparent;
+ }
+ str_appendf(repr, "%p\n", nh);
+}
+
+static void start_recursive_chain(struct nexthop_chain *nc, struct nexthop *nh)
+{
+ SET_FLAG(nc->current_top->flags, NEXTHOP_FLAG_RECURSIVE);
+ nc->current_top->resolved = nh;
+ nh->rparent = nc->current_top;
+ nc->current_recursive = nh;
+}
+static void nexthop_chain_add_recursive(struct nexthop_chain *nc)
+{
+ struct nexthop *nh;
+
+ nh = calloc(sizeof(*nh), 1);
+ assert(nh);
+
+ assert(nc->current_top);
+ if (nc->current_recursive) {
+ nc->current_recursive->next = nh;
+ nh->prev = nc->current_recursive;
+ nh->rparent = nc->current_recursive->rparent;
+ nc->current_recursive = nh;
+ } else
+ start_recursive_chain(nc, nh);
+
+ add_string_representation(&nc->repr, nh);
+}
+
+static void nexthop_chain_add_recursive_level(struct nexthop_chain *nc)
+{
+ struct nexthop *nh;
+
+ nh = calloc(sizeof(*nh), 1);
+ assert(nh);
+
+ assert(nc->current_top);
+ if (nc->current_recursive) {
+ SET_FLAG(nc->current_recursive->flags, NEXTHOP_FLAG_RECURSIVE);
+ nc->current_recursive->resolved = nh;
+ nh->rparent = nc->current_recursive;
+ nc->current_recursive = nh;
+ } else
+ start_recursive_chain(nc, nh);
+
+ add_string_representation(&nc->repr, nh);
+}
+
+static void nexthop_clear_recursive(struct nexthop *tcur)
+{
+ if (!tcur)
+ return;
+ if (CHECK_FLAG(tcur->flags, NEXTHOP_FLAG_RECURSIVE))
+ nexthop_clear_recursive(tcur->resolved);
+ if (tcur->next)
+ nexthop_clear_recursive(tcur->next);
+ free(tcur);
+}
+static void nexthop_chain_clear(struct nexthop_chain *nc)
+{
+ nexthop_clear_recursive(nc->head.nexthop);
+ nc->head.nexthop = nc->current_top = nc->current_recursive = NULL;
+ free(nc->repr);
+ nc->repr = NULL;
+}
+
+static void nexthop_chain_free(struct nexthop_chain *nc)
+{
+ if (!nc)
+ return;
+ nexthop_chain_clear(nc);
+ free(nc);
+}
+
+/* This function builds a string representation of
+ * the nexthop chain using the ALL_NEXTHOPS macro.
+ * It verifies that the ALL_NEXTHOPS macro iterated
+ * correctly over the nexthop chain by comparing the
+ * generated representation with the expected representation.
+ */
+static void nexthop_chain_verify_iter(struct nexthop_chain *nc)
+{
+ struct nexthop *nh;
+ char *repr = NULL;
+
+ for (ALL_NEXTHOPS(nc->head, nh))
+ add_string_representation(&repr, nh);
+
+ if (repr && verbose)
+ printf("===\n%s", repr);
+ assert((!repr && !nc->repr)
+ || (repr && nc->repr && !strcmp(repr, nc->repr)));
+ free(repr);
+}
+
+/* This test run builds a simple nexthop chain
+ * with some recursive nexthops and verifies that
+ * the iterator works correctly in each stage along
+ * the way.
+ */
+static void test_run_first(void)
+{
+ struct nexthop_chain *nc;
+
+ nc = nexthop_chain_new();
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_top(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_top(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_recursive(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_recursive(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_top(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_top(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_top(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_recursive(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_recursive(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_recursive(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_recursive_level(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_recursive(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_recursive(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_add_top(nc);
+ nexthop_chain_verify_iter(nc);
+
+ nexthop_chain_free(nc);
+}
+
+/* This test run builds numerous random
+ * nexthop chain configurations and verifies
+ * that the iterator correctly progresses
+ * through each. */
+static void test_run_prng(void)
+{
+ struct nexthop_chain *nc;
+ struct prng *prng;
+ int i;
+
+ nc = nexthop_chain_new();
+ prng = prng_new(0);
+
+ for (i = 0; i < 1000000; i++) {
+ switch (prng_rand(prng) % 10) {
+ case 0:
+ nexthop_chain_clear(nc);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ nexthop_chain_add_top(nc);
+ break;
+ case 6:
+ case 7:
+ case 8:
+ if (nc->current_top)
+ nexthop_chain_add_recursive(nc);
+ break;
+ case 9:
+ if (nc->current_top)
+ nexthop_chain_add_recursive_level(nc);
+ break;
+ }
+ nexthop_chain_verify_iter(nc);
+ }
+ nexthop_chain_free(nc);
+ prng_free(prng);
+}
+
+int main(int argc, char **argv)
+{
+ if (argc >= 2 && !strcmp("-v", argv[1]))
+ verbose = 1;
+ test_run_first();
+ printf("Simple test passed.\n");
+ test_run_prng();
+ printf("PRNG test passed.\n");
+}
diff --git a/tests/lib/test_nexthop_iter.py b/tests/lib/test_nexthop_iter.py
new file mode 100644
index 0000000..0c39dce
--- /dev/null
+++ b/tests/lib/test_nexthop_iter.py
@@ -0,0 +1,9 @@
+import frrtest
+
+
+class TestNexthopIter(frrtest.TestMultiOut):
+ program = "./test_nexthop_iter"
+
+
+TestNexthopIter.onesimple("Simple test passed.")
+TestNexthopIter.onesimple("PRNG test passed.")
diff --git a/tests/lib/test_ntop.c b/tests/lib/test_ntop.c
new file mode 100644
index 0000000..d357047
--- /dev/null
+++ b/tests/lib/test_ntop.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * frr_inet_ntop() unit test
+ * Copyright (C) 2019 David Lamparter
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+
+#include "tests/helpers/c/prng.h"
+
+/* NB: libfrr is NOT linked for this unit test! */
+
+#define INET_NTOP_NO_OVERRIDE
+#include "lib/ntop.c"
+
+int main(int argc, char **argv)
+{
+ size_t i, j, k, l;
+ struct in_addr i4;
+ struct in6_addr i6, i6check;
+ char buf1[64], buf2[64];
+ const char *rv;
+ struct prng *prng;
+
+ prng = prng_new(0);
+ /* IPv4 */
+ for (i = 0; i < 1000; i++) {
+ i4.s_addr = prng_rand(prng);
+ assert(frr_inet_ntop(AF_INET, &i4, buf1, sizeof(buf1)));
+ assert(inet_ntop(AF_INET, &i4, buf2, sizeof(buf2)));
+ assert(!strcmp(buf1, buf2));
+ }
+
+ /* check size limit */
+ for (i = 0; i < sizeof(buf1); i++) {
+ memset(buf2, 0xcc, sizeof(buf2));
+ rv = frr_inet_ntop(AF_INET, &i4, buf2, i);
+ if (i < strlen(buf1) + 1)
+ assert(!rv);
+ else
+ assert(rv && !strcmp(buf1, buf2));
+ }
+
+ /* IPv6 */
+ for (i = 0; i < 10000; i++) {
+ uint16_t *i6w = (uint16_t *)&i6;
+ for (j = 0; j < 8; j++)
+ i6w[j] = prng_rand(prng);
+
+ /* clear some words */
+ l = prng_rand(prng) & 7;
+ for (j = 0; j < l; j++) {
+ uint32_t num = __builtin_ctz(prng_rand(prng));
+ uint32_t where = prng_rand(prng) & 7;
+
+ for (k = where; k < where + num && k < 8; k++)
+ i6w[k] = 0;
+ }
+
+ assert(frr_inet_ntop(AF_INET6, &i6, buf1, sizeof(buf1)));
+ assert(inet_ntop(AF_INET6, &i6, buf2, sizeof(buf2)));
+ if (strcmp(buf1, buf2))
+ printf("%-40s (FRR) != (SYS) %-40s\n", buf1, buf2);
+
+ assert(inet_pton(AF_INET6, buf1, &i6check));
+ assert(!memcmp(&i6, &i6check, sizeof(i6)));
+ }
+ return 0;
+}
diff --git a/tests/lib/test_ntop.py b/tests/lib/test_ntop.py
new file mode 100644
index 0000000..69c4353
--- /dev/null
+++ b/tests/lib/test_ntop.py
@@ -0,0 +1,8 @@
+import frrtest
+
+
+class TestNtop(frrtest.TestMultiOut):
+ program = "./test_ntop"
+
+
+TestNtop.exit_cleanly()
diff --git a/tests/lib/test_plist.c b/tests/lib/test_plist.c
new file mode 100644
index 0000000..bfad766
--- /dev/null
+++ b/tests/lib/test_plist.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Simple prefix list querying tool
+ *
+ * Copyright (C) 2021 by David Lamparter,
+ * for Open Source Routing / NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#include "lib/plist.h"
+#include "lib/filter.h"
+#include "tests/lib/cli/common_cli.h"
+
+static const struct frr_yang_module_info *const my_yang_modules[] = {
+ &frr_filter_info,
+ NULL,
+};
+
+__attribute__((_CONSTRUCTOR(2000)))
+static void test_yang_modules_set(void)
+{
+ test_yang_modules = my_yang_modules;
+}
+
+void test_init(int argc, char **argv)
+{
+ prefix_list_init();
+ filter_cli_init();
+
+ /* nothing else to do here, giving stand-alone access to the prefix
+ * list code's "debug prefix-list ..." command is the only purpose of
+ * this "test".
+ */
+}
diff --git a/tests/lib/test_prefix2str.c b/tests/lib/test_prefix2str.c
new file mode 100644
index 0000000..9d837ee
--- /dev/null
+++ b/tests/lib/test_prefix2str.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * prefix2str() unit test
+ * Copyright (C) 2019 David Lamparter
+ * Portions:
+ * Copyright (C) 2019 Cumulus Networks, Inc
+ * Quentin Young
+ */
+#include <zebra.h>
+
+#include "lib/prefix.h"
+
+#include "tests/helpers/c/prng.h"
+
+int main(int argc, char **argv)
+{
+ size_t i, j, k, l;
+ struct in6_addr i6;
+ char buf1[64], buf2[64], ntopbuf[64];
+ struct prng *prng;
+ struct prefix p = {};
+
+ prng = prng_new(0);
+ /* IPv4 */
+ p.family = AF_INET;
+ for (i = 0; i < 1000; i++) {
+ p.u.prefix = prng_rand(prng);
+ p.prefixlen = prng_rand(prng) >> 26;
+ snprintf(buf1, sizeof(buf1), "%s/%d",
+ inet_ntop(AF_INET, &p.u.prefix4, ntopbuf,
+ sizeof(ntopbuf)),
+ p.prefixlen);
+ prefix2str(&p, buf2, sizeof(buf2));
+ assert(!strcmp(buf1, buf2));
+ fprintf(stdout, "%s\n", buf1);
+ }
+
+ /* IPv6 */
+ p.family = AF_INET6;
+ for (i = 0; i < 10000; i++) {
+ uint16_t *i6w = (uint16_t *)&i6;
+ for (j = 0; j < 8; j++)
+ i6w[j] = prng_rand(prng);
+
+ /* clear some words */
+ l = prng_rand(prng) & 7;
+ for (j = 0; j < l; j++) {
+ uint32_t num = __builtin_ctz(prng_rand(prng));
+ uint32_t where = prng_rand(prng) & 7;
+
+ for (k = where; k < where + num && k < 8; k++)
+ i6w[k] = 0;
+ }
+
+ p.prefixlen = prng_rand(prng) >> 24;
+ memcpy(&p.u.prefix, &i6, sizeof(i6));
+ snprintf(buf1, sizeof(buf1), "%s/%d",
+ inet_ntop(AF_INET6, &p.u.prefix6, ntopbuf,
+ sizeof(ntopbuf)),
+ p.prefixlen);
+ prefix2str(&p, buf2, sizeof(buf2));
+ assert(!strcmp(buf1, buf2));
+ fprintf(stdout, "%s\n", buf1);
+ }
+
+ return 0;
+}
diff --git a/tests/lib/test_prefix2str.py b/tests/lib/test_prefix2str.py
new file mode 100644
index 0000000..fd883ed
--- /dev/null
+++ b/tests/lib/test_prefix2str.py
@@ -0,0 +1,8 @@
+import frrtest
+
+
+class TestPrefix2str(frrtest.TestMultiOut):
+ program = "./test_prefix2str"
+
+
+TestPrefix2str.exit_cleanly()
diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c
new file mode 100644
index 0000000..66699ec
--- /dev/null
+++ b/tests/lib/test_printfrr.c
@@ -0,0 +1,409 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * printfrr() unit test
+ * Copyright (C) 2019 David Lamparter
+ */
+
+#include "zebra.h"
+
+#include <math.h>
+
+#include "lib/printfrr.h"
+#include "lib/memory.h"
+#include "lib/prefix.h"
+#include "lib/nexthop.h"
+#include "lib/asn.h"
+
+static int errors;
+
+static void printcmp(const char *fmt, ...) PRINTFRR(1, 2);
+static void printcmp(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[256], bufrr[256], *p;
+ int cmp;
+ memset(bufrr, 0xcc, sizeof(bufrr));
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ va_start(ap, fmt);
+ vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap);
+ va_end(ap);
+
+ cmp = strcmp(buf, bufrr);
+
+ /* OS dependent "+nan" vs. "nan" */
+ if (cmp && (p = strstr(bufrr, "+nan"))) {
+ p[0] = ' ';
+ if (!strcmp(buf, bufrr))
+ cmp = 0;
+ p[0] = '+';
+ }
+ printf("fmt: \"%s\"\nsys: \"%s\"\nfrr: \"%s\"\n%s\n\n",
+ fmt, buf, bufrr, cmp ? "ERROR" : "ok");
+
+ if (cmp)
+ errors++;
+}
+
+static int printchk(const char *ref, const char *fmt, ...) PRINTFRR(2, 3);
+static int printchk(const char *ref, const char *fmt, ...)
+{
+ va_list ap;
+ char bufrr[256];
+ bool truncfail = false;
+ size_t i;
+ size_t expectlen;
+
+ memset(bufrr, 0xcc, sizeof(bufrr));
+
+ va_start(ap, fmt);
+ expectlen = vsnprintfrr(NULL, 0, fmt, ap);
+ va_end(ap);
+
+ va_start(ap, fmt);
+ vsnprintfrr(bufrr, 7, fmt, ap);
+ va_end(ap);
+
+ if (strnlen(bufrr, 7) == 7)
+ truncfail = true;
+ if (strnlen(bufrr, 7) < 7 && strncmp(ref, bufrr, 6) != 0)
+ truncfail = true;
+ for (i = 7; i < sizeof(bufrr); i++)
+ if (bufrr[i] != (char)0xcc) {
+ truncfail = true;
+ break;
+ }
+
+ if (truncfail) {
+ printf("truncation test FAILED:\n"
+ "fmt: \"%s\"\nref: \"%s\"\nfrr[:7]: \"%s\"\n%s\n\n",
+ fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok");
+ errors++;
+ }
+
+ struct fmt_outpos outpos[16];
+ struct fbuf fb = {
+ .buf = bufrr,
+ .pos = bufrr,
+ .len = sizeof(bufrr) - 1,
+ .outpos = outpos,
+ .outpos_n = array_size(outpos),
+ };
+
+ va_start(ap, fmt);
+ vbprintfrr(&fb, fmt, ap);
+ fb.pos[0] = '\0';
+ va_end(ap);
+
+ printf("fmt: \"%s\"\nref: \"%s\"\nfrr: \"%s\"\n%s\n",
+ fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok");
+ if (strcmp(ref, bufrr))
+ errors++;
+ if (strlen(bufrr) != expectlen) {
+ printf("return value <> length mismatch\n");
+ errors++;
+ }
+
+ for (size_t i = 0; i < fb.outpos_i; i++)
+ printf("\t[%zu: %u..%u] = \"%.*s\"\n", i,
+ outpos[i].off_start,
+ outpos[i].off_end,
+ (int)(outpos[i].off_end - outpos[i].off_start),
+ bufrr + outpos[i].off_start);
+ printf("\n");
+ return 0;
+}
+
+static void test_va(const char *ref, const char *fmt, ...) PRINTFRR(2, 3);
+static void test_va(const char *ref, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &ap;
+
+ printchk(ref, "VA [%pVA] %s", &vaf, "--");
+
+ va_end(ap);
+}
+
+int main(int argc, char **argv)
+{
+ size_t i;
+ float flts[] = {
+ 123.456789,
+ 23.456789e-30,
+ 3.456789e+30,
+ INFINITY,
+ NAN,
+ };
+ uint64_t ui64 = 0xfeed1278cafef00d;
+ struct in_addr ip;
+ char *p;
+ char buf[256];
+ as_t asn;
+
+ printcmp("%d %u %d %u", 123, 123, -456, -456);
+ printcmp("%lld %llu %lld %llu", 123LL, 123LL, -456LL, -456LL);
+
+ printcmp("%-20s,%20s,%.20s", "test", "test", "test");
+ printcmp("%-3s,%3s,%.3s", "test", "test", "test");
+ printcmp("%-6.3s,%6.3s,%6.3s", "test", "test", "test");
+ printcmp("%*s,%*s,%.*s", -3, "test", 3, "test", 3, "test");
+
+ for (i = 0; i < array_size(flts); i++) {
+ printcmp("%-6.3e,%6.3e,%+06.3e", flts[i], flts[i], flts[i]);
+ printcmp("%-6.3f,%6.3f,%+06.3f", flts[i], flts[i], flts[i]);
+ printcmp("%-6.3g,%6.3g,%+06.3g", flts[i], flts[i], flts[i]);
+ printcmp("%-6.3a,%6.3a,%+06.3a", flts[i], flts[i], flts[i]);
+ }
+
+ printchk("-77385308584349683 18369358765125201933 feed1278cafef00d",
+ "%Ld %Lu %Lx", ui64, ui64, ui64);
+
+ FMT_NSTD(printchk("11110000000011111010010111000011", "%b", 0xf00fa5c3));
+ FMT_NSTD(printchk("0b01011010", "%#010b", 0x5a));
+
+ inet_aton("192.168.1.2", &ip);
+ printchk("192.168.1.2", "%pI4", &ip);
+ printchk(" 192.168.1.2", "%20pI4", &ip);
+ printchk("192.168.1.2 ", "%-20pI4", &ip);
+
+ printcmp("%p", &ip);
+
+ test_va("VA [192.168.1.2 1234] --", "%pI4 %u", &ip, 1234);
+
+ inet_aton("0.0.0.0", &ip);
+ printchk("0.0.0.0", "%pI4", &ip);
+ printchk("*", "%pI4s", &ip);
+
+ snprintfrr(buf, sizeof(buf), "test%s", "#1");
+ csnprintfrr(buf, sizeof(buf), "test%s", "#2");
+ assert(strcmp(buf, "test#1test#2") == 0);
+
+ p = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), "test%s", "#3");
+ assert(p == buf);
+ assert(strcmp(buf, "test#3") == 0);
+
+ p = asnprintfrr(MTYPE_TMP, buf, 4, "test%s", "#4");
+ assert(p != buf);
+ assert(strcmp(p, "test#4") == 0);
+ XFREE(MTYPE_TMP, p);
+
+ p = asprintfrr(MTYPE_TMP, "test%s", "#5");
+ assert(strcmp(p, "test#5") == 0);
+ XFREE(MTYPE_TMP, p);
+
+ struct prefix pfx;
+
+ str2prefix("192.168.1.23/24", &pfx);
+ printchk("192.168.1.23/24", "%pFX", &pfx);
+ printchk("192.168.1.23", "%pFXh", &pfx);
+
+ str2prefix("2001:db8::1234/64", &pfx);
+ printchk("2001:db8::1234/64", "%pFX", &pfx);
+ printchk("2001:db8::1234", "%pFXh", &pfx);
+
+ pfx.family = AF_UNIX;
+ printchk("UNK prefix", "%pFX", &pfx);
+ printchk("{prefix.af=AF_UNIX}", "%pFXh", &pfx);
+
+ str2prefix_eth("02:ca:fe:f0:0d:1e/48", (struct prefix_eth *)&pfx);
+ printchk("02:ca:fe:f0:0d:1e/48", "%pFX", &pfx);
+ printchk("02:ca:fe:f0:0d:1e", "%pFXh", &pfx);
+
+ struct prefix_sg sg;
+ sg.src.s_addr = INADDR_ANY;
+ sg.grp.s_addr = INADDR_ANY;
+ printchk("(*,*)", "%pPSG4", &sg);
+
+ inet_aton("192.168.1.2", &sg.src);
+ printchk("(192.168.1.2,*)", "%pPSG4", &sg);
+
+ inet_aton("224.1.2.3", &sg.grp);
+ printchk("(192.168.1.2,224.1.2.3)", "%pPSG4", &sg);
+
+ sg.src.s_addr = INADDR_ANY;
+ printchk("(*,224.1.2.3)", "%pPSG4", &sg);
+
+ uint8_t randhex[] = { 0x12, 0x34, 0x00, 0xca, 0xfe, 0x00, 0xaa, 0x55 };
+
+ FMT_NSTD(printchk("12 34 00 ca fe 00 aa 55", "%.8pHX", randhex));
+ FMT_NSTD(printchk("12 34 00 ca fe 00 aa 55", "%.*pHX",
+ (int)sizeof(randhex), randhex));
+ FMT_NSTD(printchk("12 34 00 ca", "%.4pHX", randhex));
+
+ printchk("12 34 00 ca fe 00 aa 55", "%8pHX", randhex);
+ printchk("12 34 00 ca fe 00 aa 55", "%*pHX",
+ (int)sizeof(randhex), randhex);
+ printchk("12 34 00 ca", "%4pHX", randhex);
+
+ printchk("", "%pHX", randhex);
+
+ printchk("12:34:00:ca:fe:00:aa:55", "%8pHXc", randhex);
+ printchk("123400cafe00aa55", "%8pHXn", randhex);
+
+ printchk("/test/pa\\ th/\\~spe\\ncial\\x01/file.name", "%pSE",
+ "/test/pa th/~spe\ncial\x01/file.name");
+ printchk("/test/pa\\ th/\\~spe\\n", "%17pSE",
+ "/test/pa th/~spe\ncial\x01/file.name");
+
+ char nulltest[] = { 'n', 'u', 0, 'l', 'l' };
+
+ printchk("nu\\x00ll", "%5pSE", nulltest);
+ printchk("nu\\x00ll", "%*pSE", 5, nulltest);
+
+ printchk("bl\\\"ah\\x01te[st\\nab]c", "%pSQ",
+ "bl\"ah\x01te[st\nab]c");
+ printchk("\"bl\\\"ah\\x01te[st\\nab]c\"", "%pSQq",
+ "bl\"ah\x01te[st\nab]c");
+ printchk("\"bl\\\"ah\\x01te[st\\x0aab\\]c\"", "%pSQqs",
+ "bl\"ah\x01te[st\nab]c");
+ printchk("\"\"", "%pSQqn", "");
+ printchk("\"\"", "%pSQqn", (char *)NULL);
+ printchk("(null)", "%pSQq", (char *)NULL);
+
+ /*
+ * %pNH<foo> tests
+ *
+ * gateway addresses only for now: interfaces require more setup
+ */
+ printchk("(null)", "%pNHcg", (struct nexthop *)NULL);
+ printchk("(null)", "%pNHci", (struct nexthop *)NULL);
+
+ struct nexthop nh;
+
+ memset(&nh, 0, sizeof(nh));
+
+ nh.type = NEXTHOP_TYPE_IPV4;
+ inet_aton("3.2.1.0", &nh.gate.ipv4);
+ printchk("3.2.1.0", "%pNHcg", &nh);
+
+ nh.type = NEXTHOP_TYPE_IPV6;
+ inet_pton(AF_INET6, "fe2c::34", &nh.gate.ipv6);
+ printchk("fe2c::34", "%pNHcg", &nh);
+
+ /* time printing */
+
+ /* need a non-UTC timezone for testing */
+ setenv("TZ", "TEST-01:00", 1);
+ tzset();
+
+ struct timespec ts;
+ struct timeval tv;
+ time_t tt;
+
+ ts.tv_sec = tv.tv_sec = tt = 1642015880;
+ ts.tv_nsec = 123456789;
+ tv.tv_usec = 234567;
+
+ printchk("Wed Jan 12 20:31:20 2022", "%pTSR", &ts);
+ printchk("Wed Jan 12 20:31:20 2022", "%pTVR", &tv);
+ printchk("Wed Jan 12 20:31:20 2022", "%pTTR", &tt);
+
+ FMT_NSTD(printchk("Wed Jan 12 20:31:20 2022", "%.3pTSR", &ts));
+
+ printchk("2022-01-12T20:31:20.123", "%pTSRi", &ts);
+ printchk("2022-01-12 20:31:20.123", "%pTSRip", &ts);
+ printchk("2022-01-12 20:31:20.123", "%pTSRpi", &ts);
+ FMT_NSTD(printchk("2022-01-12T20:31:20", "%.0pTSRi", &ts));
+ FMT_NSTD(printchk("2022-01-12T20:31:20.123456789", "%.9pTSRi", &ts));
+ FMT_NSTD(printchk("2022-01-12T20:31:20", "%.3pTTRi", &tt));
+
+ ts.tv_sec = tv.tv_sec = tt = 9 * 86400 + 12345;
+
+ printchk("1w 2d 03:25:45.123", "%pTSIp", &ts);
+ printchk("1w2d03:25:45.123", "%pTSI", &ts);
+ printchk("1w2d03:25:45.234", "%pTVI", &tv);
+ printchk("1w2d03:25:45", "%pTTI", &tt);
+
+ printchk("1w 2d 03h", "%pTVItp", &tv);
+ printchk("1w2d03h", "%pTSIt", &ts);
+
+ printchk("219:25:45", "%pTVIh", &tv);
+ printchk("13165:45", "%pTVIm", &tv);
+
+ ts.tv_sec = tv.tv_sec = tt = 1 * 86400 + 12345;
+
+ printchk("1d 03:25:45.123", "%pTSIp", &ts);
+ printchk("1d03:25:45.234", "%pTVI", &tv);
+
+ printchk("1d 03h 25m", "%pTVItp", &tv);
+ printchk("1d03h25m", "%pTSIt", &ts);
+
+ printchk("98745.234", "%pTVId", &tv);
+
+ printchk("27:25:45", "%pTVIh", &tv);
+ printchk("1645:45", "%pTVIm", &tv);
+
+ ts.tv_sec = tv.tv_sec = tt = 12345;
+
+ printchk("03:25:45.123", "%pTSIp", &ts);
+ printchk("03:25:45.123", "%pTSI", &ts);
+ printchk("03:25:45.234", "%pTVI", &tv);
+ printchk("03:25:45", "%pTTI", &tt);
+
+ printchk("03:25:45", "%pTSItp", &ts);
+ printchk("03:25:45", "%pTVIt", &tv);
+
+ printchk("12345.234", "%pTVId", &tv);
+
+ printchk("03:25:45", "%pTVIh", &tv);
+ printchk("205:45", "%pTVIm", &tv);
+
+ ts.tv_sec = tv.tv_sec = tt = 0;
+
+ printchk("00:00:00.123", "%pTSIp", &ts);
+ printchk("00:00:00.123", "%pTSI", &ts);
+ printchk("00:00:00.234", "%pTVI", &tv);
+ printchk("00:00:00", "%pTTI", &tt);
+
+ printchk("00:00:00", "%pTVItp", &tv);
+ printchk("00:00:00", "%pTSIt", &ts);
+
+ printchk("0.234", "%pTVId", &tv);
+ printchk("0.234", "%pTVIdx", &tv);
+ printchk("-", "%pTTIdx", &tt);
+
+ printchk("00:00:00", "%pTVIhx", &tv);
+ printchk("--:--:--", "%pTTIhx", &tt);
+ printchk("00:00", "%pTVImx", &tv);
+ printchk("--:--", "%pTTImx", &tt);
+
+ ts.tv_sec = tv.tv_sec = tt = -10;
+
+ printchk("-00:00:09.876", "%pTSIp", &ts);
+ printchk("-00:00:09.876", "%pTSI", &ts);
+ printchk("-00:00:09.765", "%pTVI", &tv);
+ printchk("-00:00:10", "%pTTI", &tt);
+
+ printchk("-00:00:09", "%pTSItp", &ts);
+ printchk("-00:00:09", "%pTSIt", &ts);
+ printchk("-00:00:09", "%pTVIt", &tv);
+ printchk("-00:00:10", "%pTTIt", &tt);
+
+ printchk("-9.765", "%pTVId", &tv);
+ printchk("-", "%pTVIdx", &tv);
+
+ printchk("-00:00:09", "%pTSIh", &ts);
+ printchk("--:--:--", "%pTVIhx", &tv);
+ printchk("--:--:--", "%pTTIhx", &tt);
+
+ printchk("-00:09", "%pTSIm", &ts);
+ printchk("--:--", "%pTVImx", &tv);
+ printchk("--:--", "%pTTImx", &tt);
+ /* ASN checks */
+ asn = 65536;
+ printchk("1.0", "%pASD", &asn);
+ asn = 65400;
+ printchk("65400", "%pASP", &asn);
+ printchk("0.65400", "%pASE", &asn);
+ printchk("65400", "%pASD", &asn);
+
+ return !!errors;
+}
diff --git a/tests/lib/test_printfrr.py b/tests/lib/test_printfrr.py
new file mode 100644
index 0000000..b8ab89e
--- /dev/null
+++ b/tests/lib/test_printfrr.py
@@ -0,0 +1,8 @@
+import frrtest
+
+
+class TestPrintfrr(frrtest.TestMultiOut):
+ program = "./test_printfrr"
+
+
+TestPrintfrr.exit_cleanly()
diff --git a/tests/lib/test_privs.c b/tests/lib/test_privs.c
new file mode 100644
index 0000000..e267548
--- /dev/null
+++ b/tests/lib/test_privs.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "privs.h"
+#include "memory.h"
+#include "lib_vty.h"
+
+zebra_capabilities_t _caps_p[] = {
+ ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, ZCAP_DAC_OVERRIDE,
+};
+
+struct zebra_privs_t test_privs = {
+#if defined(FRR_USER) && defined(FRR_GROUP)
+ .user = FRR_USER,
+ .group = FRR_GROUP,
+#endif
+#if defined(VTY_GROUP)
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0};
+
+struct option longopts[] = {{"help", no_argument, NULL, 'h'},
+ {"user", required_argument, NULL, 'u'},
+ {"group", required_argument, NULL, 'g'},
+ {0}};
+
+/* Help information display. */
+static void usage(char *progname, int status)
+{
+ if (status != 0)
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ progname);
+ else {
+ printf("Usage : %s [OPTION...]\n\
+Daemon which does 'slow' things.\n\n\
+-u, --user User to run as\n\
+-g, --group Group to run as\n\
+-h, --help Display this help and exit\n\
+\n\
+Report bugs to %s\n",
+ progname, FRR_BUG_ADDRESS);
+ }
+ exit(status);
+}
+
+struct event_loop *master;
+/* main routine. */
+int main(int argc, char **argv)
+{
+ char *p;
+ char *progname;
+ struct zprivs_ids_t ids;
+
+ /* Set umask before anything for security */
+ umask(0027);
+
+ /* get program name */
+ progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
+
+ while (1) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "hu:g:", longopts, 0);
+
+ if (opt == EOF)
+ break;
+
+ switch (opt) {
+ case 0:
+ break;
+ case 'u':
+ test_privs.user = optarg;
+ break;
+ case 'g':
+ test_privs.group = optarg;
+ break;
+ case 'h':
+ usage(progname, 0);
+ break;
+ default:
+ usage(progname, 1);
+ break;
+ }
+ }
+
+ /* Library inits. */
+ lib_cmd_init();
+ zprivs_preinit(&test_privs);
+ zprivs_init(&test_privs);
+
+#define PRIV_STATE() \
+ ((test_privs.current_state() == ZPRIVS_RAISED) ? "Raised" : "Lowered")
+
+ printf("%s\n", PRIV_STATE());
+ frr_with_privs(&test_privs) {
+ printf("%s\n", PRIV_STATE());
+ }
+
+ printf("%s\n", PRIV_STATE());
+ zprivs_get_ids(&ids);
+
+ /* terminate privileges */
+ zprivs_terminate(&test_privs);
+
+ /* but these should continue to work... */
+ printf("%s\n", PRIV_STATE());
+ frr_with_privs(&test_privs) {
+ printf("%s\n", PRIV_STATE());
+ }
+
+ printf("%s\n", PRIV_STATE());
+ zprivs_get_ids(&ids);
+
+ printf("terminating\n");
+ return 0;
+}
diff --git a/tests/lib/test_resolver.c b/tests/lib/test_resolver.c
new file mode 100644
index 0000000..d72ff4f
--- /dev/null
+++ b/tests/lib/test_resolver.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * FRR c-ares integration test
+ * Copyright (C) 2021 David Lamparter for NetDEF, Inc.
+ */
+
+/* this test is not run automatically since tests MUST NOT rely on any outside
+ * state. DNS is most definitely "outside state". A testbed may not have any
+ * internet connectivity at all. It may not have working DNS. Or worst of
+ * all, whatever name we use to test may have a temporary failure entirely
+ * beyond our control.
+ *
+ * The only way this test could be run in a testbed is with an all-local DNS
+ * setup, which considering the resolver code is rarely touched is not worth
+ * the time at all. Instead, after touching the resolver code, manually run
+ * this test and throw some names at it.
+ */
+
+#include <zebra.h>
+
+#include "vty.h"
+#include "command.h"
+#include "resolver.h"
+#include "log.h"
+#include "sockunion.h"
+
+#include "tests/lib/cli/common_cli.h"
+
+extern struct event_loop *master;
+
+static void resolver_result(struct resolver_query *resq, const char *errstr,
+ int numaddrs, union sockunion *addr)
+{
+ int i;
+
+ if (numaddrs <= 0) {
+ zlog_warn("hostname resolution failed: %s", errstr);
+ return;
+ }
+
+ for (i = 0; i < numaddrs; i++)
+ zlog_info("resolver result: %pSU", &addr[i]);
+}
+
+struct resolver_query query;
+
+DEFUN (test_resolve,
+ test_resolve_cmd,
+ "resolve WORD",
+ "DNS resolver\n"
+ "Name to resolve\n")
+{
+ resolver_resolve(&query, AF_UNSPEC, 0, argv[1]->arg, resolver_result);
+ return CMD_SUCCESS;
+}
+
+__attribute__((_CONSTRUCTOR(2000)))
+static void test_setup(void)
+{
+ test_log_prio = LOG_DEBUG;
+}
+
+void test_init(int argc, char **argv)
+{
+ resolver_init(master);
+
+ install_element(VIEW_NODE, &test_resolve_cmd);
+}
diff --git a/tests/lib/test_ringbuf.c b/tests/lib/test_ringbuf.c
new file mode 100644
index 0000000..0d7fed7
--- /dev/null
+++ b/tests/lib/test_ringbuf.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Circular buffer tests.
+ * Copyright (C) 2017 Cumulus Networks
+ * Quentin Young
+ */
+#include <zebra.h>
+#include <memory.h>
+#include "ringbuf.h"
+
+static void validate_state(struct ringbuf *buf, size_t size, size_t contains)
+{
+ assert(buf->size == size);
+ assert(ringbuf_remain(buf) == contains);
+ assert(ringbuf_space(buf) == buf->size - contains);
+ assert(buf->empty != (bool)contains);
+}
+
+int main(int argc, char **argv)
+{
+ struct ringbuf *soil = ringbuf_new(BUFSIZ);
+
+ validate_state(soil, BUFSIZ, 0);
+
+ /* verify reset functionality on clean buffer */
+ printf("Validating reset on empty buffer...\n");
+ ringbuf_reset(soil);
+
+ validate_state(soil, BUFSIZ, 0);
+
+ /* put one byte */
+ printf("Validating write...\n");
+ uint8_t walnut = 47;
+ assert(ringbuf_put(soil, &walnut, sizeof(walnut)) == 1);
+
+ validate_state(soil, BUFSIZ, 1);
+
+ /* validate read limitations */
+ printf("Validating read limits...\n");
+ uint8_t nuts[2];
+ assert(ringbuf_get(soil, &nuts, sizeof(nuts)) == 1);
+
+ /* reset */
+ printf("Validating reset on full buffer...\n");
+ ringbuf_reset(soil);
+ validate_state(soil, BUFSIZ, 0);
+
+ /* copy stack garbage to buffer */
+ printf("Validating big write...\n");
+ uint8_t compost[BUFSIZ];
+ assert(ringbuf_put(soil, &compost, sizeof(compost)) == BUFSIZ);
+
+ validate_state(soil, BUFSIZ, BUFSIZ);
+ assert(soil->start == 0);
+ assert(soil->end == 0);
+
+ /* read 15 bytes of garbage */
+ printf("Validating read...\n");
+ assert(ringbuf_get(soil, &compost, 15) == 15);
+
+ validate_state(soil, BUFSIZ, BUFSIZ - 15);
+ assert(soil->start == 15);
+ assert(soil->end == 0);
+
+ /* put another 10 bytes and validate wraparound */
+ printf("Validating wraparound...\n");
+ assert(ringbuf_put(soil, &compost[BUFSIZ/2], 10) == 10);
+
+ validate_state(soil, BUFSIZ, BUFSIZ - 15 + 10);
+ assert(soil->start == 15);
+ assert(soil->end == 10);
+
+ /* put another 15 bytes and validate state */
+ printf("Validating size limits...\n");
+ assert(ringbuf_put(soil, &compost, 15) == 5);
+ validate_state(soil, BUFSIZ, BUFSIZ);
+
+ /* read entire buffer */
+ printf("Validating big read...\n");
+ assert(ringbuf_get(soil, &compost, BUFSIZ) == BUFSIZ);
+
+ validate_state(soil, BUFSIZ, 0);
+ assert(soil->empty == true);
+ assert(soil->start == soil->end);
+ assert(soil->start == 15);
+
+ /* read empty buffer */
+ printf("Validating empty read...\n");
+ assert(ringbuf_get(soil, &compost, 1) == 0);
+ validate_state(soil, BUFSIZ, 0);
+
+ /* reset, validate state */
+ printf("Validating reset...\n");
+ ringbuf_reset(soil);
+ validate_state(soil, BUFSIZ, 0);
+ assert(soil->start == 0);
+ assert(soil->end == 0);
+
+ /* wipe, validate state */
+ printf("Validating wipe...\n");
+ memset(&compost, 0x00, sizeof(compost));
+ ringbuf_wipe(soil);
+ assert(memcmp(&compost, soil->data, sizeof(compost)) == 0);
+
+ /* validate maximum write */
+ printf("Validating very big write...\n");
+ const char flower[BUFSIZ * 2];
+ assert(ringbuf_put(soil, &flower, sizeof(flower)) == BUFSIZ);
+
+ validate_state(soil, BUFSIZ, BUFSIZ);
+
+ /* wipe, validate state */
+ printf("Validating wipe...\n");
+ memset(&compost, 0x00, sizeof(compost));
+ ringbuf_wipe(soil);
+ assert(memcmp(&compost, soil->data, sizeof(compost)) == 0);
+
+ /* validate simple data encode / decode */
+ const char *organ = "seed";
+ printf("Encoding: '%s'\n", organ);
+ assert(ringbuf_put(soil, organ, strlen(organ)) == 4);
+ char water[strlen(organ) + 1];
+ assert(ringbuf_get(soil, &water, strlen(organ)) == 4);
+ water[strlen(organ)] = '\0';
+ printf("Retrieved: '%s'\n", water);
+
+ validate_state(soil, BUFSIZ, 0);
+
+ /* validate simple data encode / decode across ring boundary */
+ soil->start = soil->size - 2;
+ soil->end = soil->start;
+ const char *phloem = "root";
+ printf("Encoding: '%s'\n", phloem);
+ assert(ringbuf_put(soil, phloem, strlen(phloem)) == 4);
+ char xylem[strlen(phloem) + 1];
+ assert(ringbuf_get(soil, &xylem, 100) == 4);
+ xylem[strlen(phloem)] = '\0';
+ printf("Retrieved: '%s'\n", xylem);
+
+ ringbuf_wipe(soil);
+
+ /* validate simple data peek across ring boundary */
+ soil->start = soil->size - 2;
+ soil->end = soil->start;
+ const char *cytoplasm = "tree";
+ printf("Encoding: '%s'\n", cytoplasm);
+ assert(ringbuf_put(soil, cytoplasm, strlen(cytoplasm)) == 4);
+ char chloroplast[strlen(cytoplasm) + 1];
+ assert(ringbuf_peek(soil, 2, &chloroplast[0], 100) == 2);
+ assert(ringbuf_peek(soil, 0, &chloroplast[2], 2) == 2);
+ chloroplast[strlen(cytoplasm)] = '\0';
+ assert(!strcmp(chloroplast, "eetr"));
+ printf("Retrieved: '%s'\n", chloroplast);
+
+ printf("Deleting...\n");
+ ringbuf_del(soil);
+
+ printf("Creating new buffer...\n");
+ soil = ringbuf_new(15);
+ soil->start = soil->end = 7;
+
+ /* validate data encode of excessive data */
+ const char *twenty = "vascular plants----";
+ char sixteen[16];
+ printf("Encoding: %s\n", twenty);
+ assert(ringbuf_put(soil, twenty, strlen(twenty)) == 15);
+ assert(ringbuf_get(soil, sixteen, 20));
+ sixteen[15] = '\0';
+ printf("Retrieved: %s\n", sixteen);
+ assert(!strcmp(sixteen, "vascular plants"));
+
+ printf("Deleting...\n");
+ ringbuf_del(soil);
+
+ printf("Done.\n");
+ return 0;
+}
diff --git a/tests/lib/test_ringbuf.py b/tests/lib/test_ringbuf.py
new file mode 100644
index 0000000..0cd9dee
--- /dev/null
+++ b/tests/lib/test_ringbuf.py
@@ -0,0 +1,8 @@
+import frrtest
+
+
+class TestRingbuf(frrtest.TestMultiOut):
+ program = "./test_ringbuf"
+
+
+TestRingbuf.exit_cleanly()
diff --git a/tests/lib/test_segv.c b/tests/lib/test_segv.c
new file mode 100644
index 0000000..5d2f451
--- /dev/null
+++ b/tests/lib/test_segv.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SEGV / backtrace handling test.
+ *
+ * copied from test-sig.c
+ *
+ * Copyright (C) 2013 by David Lamparter, Open Source Routing.
+ * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of Quagga
+ */
+
+#include <zebra.h>
+#include <sigevent.h>
+#include "lib/log.h"
+#include "lib/memory.h"
+
+struct frr_signal_t sigs[] = {};
+
+struct event_loop *master;
+
+void func1(int *arg);
+void func3(void);
+
+void func1(int *arg)
+{
+ int *null = NULL;
+ *null += 1;
+ *arg = 1;
+}
+
+static void func2(size_t depth, int *arg)
+{
+ /* variable stack frame size */
+ int buf[depth];
+ for (size_t i = 0; i < depth; i++)
+ buf[i] = arg[i] + 1;
+ if (depth > 0)
+ func2(depth - 1, buf);
+ else
+ func1(&buf[0]);
+ for (size_t i = 0; i < depth; i++)
+ buf[i] = arg[i] + 2;
+}
+
+void func3(void)
+{
+ int buf[6];
+ func2(6, buf);
+}
+
+static void threadfunc(struct event *thread)
+{
+ func3();
+}
+
+int main(void)
+{
+ master = event_master_create(NULL);
+ signal_init(master, array_size(sigs), sigs);
+
+ zlog_aux_init("NONE: ", LOG_DEBUG);
+
+ event_execute(master, threadfunc, 0, 0, NULL);
+
+ exit(0);
+}
diff --git a/tests/lib/test_seqlock.c b/tests/lib/test_seqlock.c
new file mode 100644
index 0000000..288d4a8
--- /dev/null
+++ b/tests/lib/test_seqlock.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * basic test for seqlock
+ *
+ * Copyright (C) 2015 David Lamparter
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/uio.h>
+
+#include "monotime.h"
+#include "seqlock.h"
+#include "printfrr.h"
+
+static struct seqlock sqlo;
+static pthread_t thr1;
+static struct timeval start;
+
+static void writestr(const char *str)
+{
+ struct iovec iov[2];
+ char buf[32];
+ int64_t usec = monotime_since(&start, NULL);
+
+ snprintfrr(buf, sizeof(buf), "[%02" PRId64 "] ", usec / 100000);
+
+ iov[0].iov_base = buf;
+ iov[0].iov_len = strlen(buf);
+ iov[1].iov_base = (char *)str;
+ iov[1].iov_len = strlen(str);
+ writev(1, iov, 2);
+}
+
+static void *thr1func(void *arg)
+{
+ assert(!seqlock_held(&sqlo));
+ assert(seqlock_check(&sqlo, 1));
+ seqlock_wait(&sqlo, 1);
+ writestr("thr1 (unheld)\n");
+
+ sleep(2);
+
+ assert(seqlock_held(&sqlo));
+ assert(seqlock_check(&sqlo, 1));
+ seqlock_wait(&sqlo, 1);
+ writestr("thr1 @1\n");
+
+ seqlock_wait(&sqlo, 5);
+ writestr("thr1 @5\n");
+
+ seqlock_wait(&sqlo, 9);
+ writestr("thr1 @9\n");
+
+ seqlock_wait(&sqlo, 13);
+ writestr("thr1 @13\n");
+
+ seqlock_wait(&sqlo, 17);
+ writestr("thr1 @17\n");
+
+ seqlock_wait(&sqlo, 21);
+ writestr("thr1 @21\n");
+ return NULL;
+}
+
+int main(int argc, char **argv)
+{
+ monotime(&start);
+
+ seqlock_init(&sqlo);
+
+ assert(!seqlock_held(&sqlo));
+ seqlock_acquire_val(&sqlo, 1);
+ assert(seqlock_held(&sqlo));
+
+ assert(seqlock_cur(&sqlo) == 1);
+ assert(seqlock_bump(&sqlo) == 1);
+ assert(seqlock_cur(&sqlo) == 5);
+ assert(seqlock_bump(&sqlo) == 5);
+ assert(seqlock_bump(&sqlo) == 9);
+ assert(seqlock_bump(&sqlo) == 13);
+ assert(seqlock_cur(&sqlo) == 17);
+
+ assert(seqlock_held(&sqlo));
+ seqlock_release(&sqlo);
+ assert(!seqlock_held(&sqlo));
+
+ pthread_create(&thr1, NULL, thr1func, NULL);
+ sleep(1);
+
+ writestr("main @5\n");
+ seqlock_acquire_val(&sqlo, 5);
+ sleep(2);
+
+ writestr("main @9\n");
+ seqlock_bump(&sqlo);
+ sleep(1);
+
+ writestr("main @17\n");
+ seqlock_acquire_val(&sqlo, 17);
+ sleep(1);
+
+ writestr("main @release\n");
+ seqlock_release(&sqlo);
+ sleep(1);
+}
diff --git a/tests/lib/test_sig.c b/tests/lib/test_sig.c
new file mode 100644
index 0000000..2bd7f30
--- /dev/null
+++ b/tests/lib/test_sig.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+#include <zebra.h>
+#include <sigevent.h>
+#include "lib/log.h"
+#include "lib/memory.h"
+
+static void sighup(void)
+{
+ printf("processed hup\n");
+}
+
+static void sigusr1(void)
+{
+ printf("processed usr1\n");
+}
+
+static void sigusr2(void)
+{
+ printf("processed usr2\n");
+}
+
+struct frr_signal_t sigs[] = {{
+ .signal = SIGHUP,
+ .handler = &sighup,
+ },
+ {
+ .signal = SIGUSR1,
+ .handler = &sigusr1,
+ },
+ {
+ .signal = SIGUSR2,
+ .handler = &sigusr2,
+ }};
+
+struct event_loop *master;
+struct event t;
+
+int main(void)
+{
+ master = event_master_create(NULL);
+ signal_init(master, array_size(sigs), sigs);
+
+ zlog_aux_init("NONE: ", LOG_DEBUG);
+
+ while (event_fetch(master, &t))
+ event_call(&t);
+
+ exit(0);
+}
diff --git a/tests/lib/test_skiplist.c b/tests/lib/test_skiplist.c
new file mode 100644
index 0000000..6af7ac5
--- /dev/null
+++ b/tests/lib/test_skiplist.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021, LabN Consulting, L.L.C
+ */
+
+#include <zebra.h>
+#include <skiplist.h>
+
+static void sl_debug(struct skiplist *l)
+{
+ int i;
+
+ if (!l)
+ return;
+
+ printf("Skiplist %p has max level %d\n", l, l->level);
+ for (i = l->level; i >= 0; --i)
+ printf(" @%d: %d\n", i, l->level_stats[i]);
+}
+
+static void *scramble(int i)
+{
+ uintptr_t result;
+
+ result = (uintptr_t)(i & 0xff) << 24;
+ result |= (uintptr_t)i >> 8;
+
+ return (void *)result;
+}
+#define sampleSize 65536
+static int sl_test(void)
+{
+ struct skiplist *l;
+ register int i, k;
+ void *keys[sampleSize];
+ void *v = NULL;
+ int errors = 0;
+
+ l = skiplist_new(SKIPLIST_FLAG_ALLOW_DUPLICATES, NULL, NULL);
+
+ printf("%s: skiplist_new returned %p\n", __func__, l);
+
+ for (i = 0; i < 4; i++) {
+
+ for (k = 0; k < sampleSize; k++) {
+ if (!(k % 10000))
+ printf("%s: (%d:%d)\n", __func__, i, k);
+ /* keys[k] = (void *)random(); */
+ keys[k] = scramble(k);
+ if (skiplist_insert(l, keys[k], keys[k])) {
+ ++errors;
+ printf("error in insert #%d,#%d\n", i, k);
+ }
+ }
+
+ printf("%s: inserts done\n", __func__);
+ sl_debug(l);
+
+ for (k = 0; k < sampleSize; k++) {
+
+ if (!(k % 10000))
+ printf("[%d:%d]\n", i, k);
+ /* keys[k] = (void *)random(); */
+ if (skiplist_search(l, keys[k], &v)) {
+ ++errors;
+ printf("error in search #%d,#%d\n", i, k);
+ }
+
+ if (v != keys[k]) {
+ ++errors;
+ printf("search returned wrong value\n");
+ }
+ }
+ printf("%s: searches done\n", __func__);
+
+
+ for (k = 0; k < sampleSize; k++) {
+
+ if (!(k % 10000))
+ printf("<%d:%d>\n", i, k);
+ /* keys[k] = (void *)random(); */
+ if (skiplist_delete(l, keys[k], keys[k])) {
+ ++errors;
+ printf("error in delete\n");
+ }
+ keys[k] = scramble(k ^ 0xf0f0f0f0);
+ if (skiplist_insert(l, keys[k], keys[k])) {
+ ++errors;
+ printf("error in insert #%d,#%d\n", i, k);
+ }
+ }
+
+ printf("%s: del+inserts done\n", __func__);
+ sl_debug(l);
+
+ for (k = 0; k < sampleSize; k++) {
+
+ if (!(k % 10000))
+ printf("{%d:%d}\n", i, k);
+ /* keys[k] = (void *)random(); */
+ if (skiplist_delete_first(l)) {
+ ++errors;
+ printf("error in delete_first\n");
+ }
+ }
+ }
+
+ sl_debug(l);
+
+ skiplist_free(l);
+
+ return errors;
+}
+
+int main(int argc, char **argv)
+{
+ int errors = sl_test();
+
+ if (errors)
+ return 1;
+ return 0;
+}
diff --git a/tests/lib/test_srcdest_table.c b/tests/lib/test_srcdest_table.c
new file mode 100644
index 0000000..6d6c515
--- /dev/null
+++ b/tests/lib/test_srcdest_table.c
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Test srcdest table for correctness.
+ *
+ * Copyright (C) 2017 by David Lamparter & Christian Franke,
+ * Open Source Routing / NetDEF Inc.
+ *
+ * This file is part of FRRouting (FRR)
+ */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "memory.h"
+#include "prefix.h"
+#include "prng.h"
+#include "srcdest_table.h"
+#include "table.h"
+
+/* Copied from ripngd/ripng_nexthop.h - maybe the whole s6_addr32 thing
+ * should be added by autoconf if not present?
+ */
+#ifndef s6_addr32
+#define s6_addr32 __u6_addr.__u6_addr32
+#endif /*s6_addr32*/
+
+struct event_loop *master;
+
+/* This structure is copied from lib/srcdest_table.c to which it is
+ * private as far as other parts of Quagga are concerned.
+ */
+struct srcdest_rnode {
+ /* must be first in structure for casting to/from route_node */
+ ROUTE_NODE_FIELDS;
+
+ struct route_table *src_table;
+};
+
+struct test_state {
+ struct route_table *table;
+ struct hash *log;
+};
+
+static char *format_srcdest(const struct prefix_ipv6 *dst_p,
+ const struct prefix_ipv6 *src_p)
+{
+ char dst_str[BUFSIZ];
+ char src_str[BUFSIZ];
+ char *rv;
+ int ec;
+
+ prefix2str((const struct prefix *)dst_p, dst_str, sizeof(dst_str));
+ if (src_p && src_p->prefixlen)
+ prefix2str((const struct prefix *)src_p, src_str,
+ sizeof(src_str));
+ else
+ src_str[0] = '\0';
+
+ ec = asprintf(&rv, "%s%s%s", dst_str,
+ (src_str[0] != '\0') ? " from " : "", src_str);
+
+ assert(ec > 0);
+ return rv;
+}
+
+static unsigned int log_key(const void *data)
+{
+ const struct prefix *hash_entry = data;
+ struct prefix_ipv6 *dst_p = (struct prefix_ipv6 *)&hash_entry[0];
+ struct prefix_ipv6 *src_p = (struct prefix_ipv6 *)&hash_entry[1];
+ unsigned int hash = 0;
+ unsigned int i;
+
+ hash = (hash * 33) ^ (unsigned int)dst_p->prefixlen;
+ for (i = 0; i < 4; i++)
+ hash = (hash * 33) ^ (unsigned int)dst_p->prefix.s6_addr32[i];
+
+ hash = (hash * 33) ^ (unsigned int)src_p->prefixlen;
+ if (src_p->prefixlen)
+ for (i = 0; i < 4; i++)
+ hash = (hash * 33)
+ ^ (unsigned int)src_p->prefix.s6_addr32[i];
+
+ return hash;
+}
+
+static bool log_cmp(const void *a, const void *b)
+{
+ if (a == NULL || b == NULL)
+ return false;
+
+ return !memcmp(a, b, 2 * sizeof(struct prefix));
+}
+
+static void log_free(void *data)
+{
+ XFREE(MTYPE_TMP, data);
+}
+
+static void *log_alloc(void *data)
+{
+ void *rv = XMALLOC(MTYPE_TMP, 2 * sizeof(struct prefix));
+ memcpy(rv, data, 2 * sizeof(struct prefix));
+ return rv;
+}
+
+static struct test_state *test_state_new(void)
+{
+ struct test_state *rv;
+
+ rv = XCALLOC(MTYPE_TMP, sizeof(*rv));
+ assert(rv);
+
+ rv->table = srcdest_table_init();
+ assert(rv->table);
+
+ rv->log = hash_create(log_key, log_cmp, NULL);
+ return rv;
+}
+
+static void test_state_free(struct test_state *test)
+{
+ route_table_finish(test->table);
+ hash_clean_and_free(&test->log, log_free);
+ XFREE(MTYPE_TMP, test);
+}
+
+static void test_state_add_route(struct test_state *test,
+ struct prefix_ipv6 *dst_p,
+ struct prefix_ipv6 *src_p)
+{
+ struct route_node *rn =
+ srcdest_rnode_get(test->table, (struct prefix *)dst_p, src_p);
+ struct prefix hash_entry[2];
+
+ memset(hash_entry, 0, sizeof(hash_entry));
+ memcpy(&hash_entry[0], dst_p, sizeof(*dst_p));
+ memcpy(&hash_entry[1], src_p, sizeof(*src_p));
+
+ if (rn->info) {
+ route_unlock_node(rn);
+ assert(hash_lookup(test->log, hash_entry) != NULL);
+ return;
+ } else {
+ assert(hash_lookup(test->log, hash_entry) == NULL);
+ }
+
+ rn->info = (void *)0xdeadbeef;
+ (void)hash_get(test->log, hash_entry, log_alloc);
+};
+
+static void test_state_del_route(struct test_state *test,
+ struct prefix_ipv6 *dst_p,
+ struct prefix_ipv6 *src_p)
+{
+ struct route_node *rn = srcdest_rnode_lookup(
+ test->table, (struct prefix *)dst_p, src_p);
+ struct prefix hash_entry[2];
+
+ memset(hash_entry, 0, sizeof(hash_entry));
+ memcpy(&hash_entry[0], dst_p, sizeof(*dst_p));
+ memcpy(&hash_entry[1], src_p, sizeof(*src_p));
+
+ if (!rn) {
+ assert(!hash_lookup(test->log, hash_entry));
+ return;
+ }
+
+ assert(rn->info == (void *)0xdeadbeef);
+ rn->info = NULL;
+ route_unlock_node(rn);
+ route_unlock_node(rn);
+
+ struct prefix *hash_entry_intern = hash_release(test->log, hash_entry);
+ assert(hash_entry_intern != NULL);
+ XFREE(MTYPE_TMP, hash_entry_intern);
+}
+
+static void verify_log(struct hash_bucket *bucket, void *arg)
+{
+ struct test_state *test = arg;
+ struct prefix *hash_entry = bucket->data;
+ struct prefix *dst_p = &hash_entry[0];
+ struct prefix_ipv6 *src_p = (struct prefix_ipv6 *)&hash_entry[1];
+ struct route_node *rn = srcdest_rnode_lookup(test->table, dst_p, src_p);
+
+ assert(rn);
+ assert(rn->info == (void *)0xdeadbeef);
+
+ route_unlock_node(rn);
+}
+
+static void dump_log(struct hash_bucket *bucket, void *arg)
+{
+ struct prefix *hash_entry = bucket->data;
+ struct prefix_ipv6 *dst_p = (struct prefix_ipv6 *)&hash_entry[0];
+ struct prefix_ipv6 *src_p = (struct prefix_ipv6 *)&hash_entry[1];
+ char *route_id = format_srcdest(dst_p, src_p);
+
+ fprintf(stderr, " %s\n", route_id);
+ free(route_id);
+}
+
+static void test_dump(struct test_state *test)
+{
+ fprintf(stderr, "Contents of hash table:\n");
+ hash_iterate(test->log, dump_log, test);
+ fprintf(stderr, "\n");
+}
+
+static void test_failed(struct test_state *test, const char *message,
+ const struct prefix_ipv6 *dst_p,
+ const struct prefix_ipv6 *src_p)
+{
+ char *route_id = format_srcdest(dst_p, src_p);
+
+ fprintf(stderr, "Test failed. Error: %s\n", message);
+ fprintf(stderr, "Route in question: %s\n", route_id);
+ free(route_id);
+
+ test_dump(test);
+ assert(3 == 4);
+}
+
+static void test_state_verify(struct test_state *test)
+{
+ struct route_node *rn;
+ struct prefix hash_entry[2];
+
+ memset(hash_entry, 0, sizeof(hash_entry));
+
+ /* Verify that there are no elements in the table which have never
+ * been added */
+ for (rn = route_top(test->table); rn; rn = srcdest_route_next(rn)) {
+ const struct prefix_ipv6 *dst_p, *src_p;
+
+ /* While we are iterating, we hold a lock on the current
+ * route_node,
+ * so all the lock counts we check for take that into account;
+ * in idle
+ * state all the numbers will be exactly one less.
+ *
+ * Also this makes quite some assumptions based on the current
+ * implementation details of route_table and srcdest_table -
+ * another
+ * valid implementation might trigger assertions here.
+ */
+
+ if (rnode_is_dstnode(rn)) {
+ struct srcdest_rnode *srn = (struct srcdest_rnode *)rn;
+ unsigned int expected_lock = 1; /* We are in the loop */
+
+ if (rn->info
+ != NULL) /* The route node is not internal */
+ expected_lock++;
+ if (srn->src_table != NULL) /* There's a source table
+ associated with rn */
+ expected_lock++;
+
+ if (route_node_get_lock_count(rn) != expected_lock)
+ test_failed(
+ test,
+ "Dest rnode lock count doesn't match expected count!",
+ (struct prefix_ipv6 *)&rn->p, NULL);
+ } else {
+ unsigned int expected_lock = 1; /* We are in the loop */
+
+ if (rn->info
+ != NULL) /* The route node is not internal */
+ expected_lock++;
+
+ if (route_node_get_lock_count(rn) != expected_lock) {
+ srcdest_rnode_prefixes(
+ rn, (const struct prefix **)&dst_p,
+ (const struct prefix **)&src_p);
+
+ test_failed(
+ test,
+ "Src rnode lock count doesn't match expected count!",
+ dst_p, src_p);
+ }
+ }
+
+ if (!rn->info)
+ continue;
+
+ assert(rn->info == (void *)0xdeadbeef);
+
+ srcdest_rnode_prefixes(rn, (const struct prefix **)&dst_p,
+ (const struct prefix **)&src_p);
+ memcpy(&hash_entry[0], dst_p, sizeof(*dst_p));
+ if (src_p)
+ memcpy(&hash_entry[1], src_p, sizeof(*src_p));
+ else
+ memset(&hash_entry[1], 0, sizeof(hash_entry[1]));
+
+ if (hash_lookup(test->log, hash_entry) == NULL)
+ test_failed(test, "Route is missing in hash", dst_p,
+ src_p);
+ }
+
+ /* Verify that all added elements are still in the table */
+ hash_iterate(test->log, verify_log, test);
+}
+
+static void get_rand_prefix(struct prng *prng, struct prefix_ipv6 *p)
+{
+ int i;
+
+ memset(p, 0, sizeof(*p));
+
+ for (i = 0; i < 4; i++)
+ p->prefix.s6_addr32[i] = prng_rand(prng);
+ p->prefixlen = prng_rand(prng) % 129;
+ p->family = AF_INET6;
+
+ apply_mask(p);
+}
+
+static void get_rand_prefix_pair(struct prng *prng, struct prefix_ipv6 *dst_p,
+ struct prefix_ipv6 *src_p)
+{
+ get_rand_prefix(prng, dst_p);
+ if ((prng_rand(prng) % 4) == 0) {
+ get_rand_prefix(prng, src_p);
+ if (src_p->prefixlen)
+ return;
+ }
+
+ memset(src_p, 0, sizeof(*src_p));
+}
+
+static void test_state_add_rand_route(struct test_state *test,
+ struct prng *prng)
+{
+ struct prefix_ipv6 dst_p, src_p;
+
+ get_rand_prefix_pair(prng, &dst_p, &src_p);
+ test_state_add_route(test, &dst_p, &src_p);
+}
+
+static void test_state_del_rand_route(struct test_state *test,
+ struct prng *prng)
+{
+ struct prefix_ipv6 dst_p, src_p;
+
+ get_rand_prefix_pair(prng, &dst_p, &src_p);
+ test_state_del_route(test, &dst_p, &src_p);
+}
+
+static void test_state_del_one_route(struct test_state *test, struct prng *prng)
+{
+ unsigned int which_route;
+
+ if (test->log->count == 0)
+ return;
+
+ which_route = prng_rand(prng) % test->log->count;
+
+ struct route_node *rn;
+ const struct prefix *dst_p, *src_p;
+ struct prefix_ipv6 dst6_p, src6_p;
+
+ for (rn = route_top(test->table); rn; rn = srcdest_route_next(rn)) {
+ if (!rn->info)
+ continue;
+ if (!which_route) {
+ route_unlock_node(rn);
+ break;
+ }
+ which_route--;
+ }
+
+ assert(rn);
+ srcdest_rnode_prefixes(rn, &dst_p, &src_p);
+ memcpy(&dst6_p, dst_p, sizeof(dst6_p));
+ if (src_p)
+ memcpy(&src6_p, src_p, sizeof(src6_p));
+ else
+ memset(&src6_p, 0, sizeof(src6_p));
+
+ test_state_del_route(test, &dst6_p, &src6_p);
+}
+
+static void run_prng_test(void)
+{
+ struct test_state *test = test_state_new();
+ struct prng *prng = prng_new(0);
+ size_t i;
+
+ for (i = 0; i < 1000; i++) {
+ switch (prng_rand(prng) % 10) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ test_state_add_rand_route(test, prng);
+ break;
+ case 5:
+ case 6:
+ case 7:
+ test_state_del_one_route(test, prng);
+ break;
+ case 8:
+ case 9:
+ test_state_del_rand_route(test, prng);
+ break;
+ }
+ test_state_verify(test);
+ }
+
+ prng_free(prng);
+ test_state_free(test);
+}
+
+int main(int argc, char *argv[])
+{
+ run_prng_test();
+ printf("PRNG Test successful.\n");
+ return 0;
+}
diff --git a/tests/lib/test_srcdest_table.py b/tests/lib/test_srcdest_table.py
new file mode 100644
index 0000000..d0dde6a
--- /dev/null
+++ b/tests/lib/test_srcdest_table.py
@@ -0,0 +1,8 @@
+import frrtest
+
+
+class TestSrcdestTable(frrtest.TestMultiOut):
+ program = "./test_srcdest_table"
+
+
+TestSrcdestTable.onesimple("PRNG Test successful.")
diff --git a/tests/lib/test_stream.c b/tests/lib/test_stream.c
new file mode 100644
index 0000000..d38dfe0
--- /dev/null
+++ b/tests/lib/test_stream.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Simple stream test.
+ *
+ * Copyright (C) 2006 Sun Microsystems, Inc.
+ */
+
+#include <zebra.h>
+#include <stream.h>
+#include "frrevent.h"
+
+#include "printfrr.h"
+
+static unsigned long long ham = 0xdeadbeefdeadbeef;
+struct event_loop *master;
+
+static void print_stream(struct stream *s)
+{
+ size_t getp = stream_get_getp(s);
+
+ printfrr("endp: %zu, readable: %zu, writeable: %zu\n",
+ stream_get_endp(s), STREAM_READABLE(s), STREAM_WRITEABLE(s));
+
+ while (STREAM_READABLE(s)) {
+ printfrr("0x%x ", *stream_pnt(s));
+ stream_forward_getp(s, 1);
+ }
+
+ printfrr("\n");
+
+ /* put getp back to where it was */
+ stream_set_getp(s, getp);
+}
+
+int main(void)
+{
+ struct stream *s;
+
+ s = stream_new(1024);
+
+ stream_putc(s, ham);
+ stream_putw(s, ham);
+ stream_putl(s, ham);
+ stream_putq(s, ham);
+
+ print_stream(s);
+
+ stream_resize_inplace(&s, stream_get_endp(s));
+
+ print_stream(s);
+
+ printfrr("c: 0x%hhx\n", stream_getc(s));
+ printfrr("w: 0x%hx\n", stream_getw(s));
+ printfrr("l: 0x%x\n", stream_getl(s));
+ printfrr("q: 0x%" PRIx64 "\n", stream_getq(s));
+
+ return 0;
+}
diff --git a/tests/lib/test_stream.py b/tests/lib/test_stream.py
new file mode 100644
index 0000000..11d902e
--- /dev/null
+++ b/tests/lib/test_stream.py
@@ -0,0 +1,5 @@
+import frrtest
+
+
+class TestStream(frrtest.TestRefOut):
+ program = "./test_stream"
diff --git a/tests/lib/test_stream.refout b/tests/lib/test_stream.refout
new file mode 100644
index 0000000..cf52e13
--- /dev/null
+++ b/tests/lib/test_stream.refout
@@ -0,0 +1,8 @@
+endp: 15, readable: 15, writeable: 1009
+0xef 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef
+endp: 15, readable: 15, writeable: 0
+0xef 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef
+c: 0xef
+w: 0xbeef
+l: 0xdeadbeef
+q: 0xdeadbeefdeadbeef
diff --git a/tests/lib/test_table.c b/tests/lib/test_table.c
new file mode 100644
index 0000000..51bfda2
--- /dev/null
+++ b/tests/lib/test_table.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Routing table test
+ * Copyright (C) 2012 OSR.
+ *
+ * This file is part of Quagga
+ */
+
+#include <zebra.h>
+#include "printfrr.h"
+#include "prefix.h"
+#include "table.h"
+
+/*
+ * test_node_t
+ *
+ * Information that is kept for each node in the radix tree.
+ */
+typedef struct test_node_t_ {
+
+ /*
+ * Human readable representation of the string. Allocated using
+ * malloc()/dup().
+ */
+ char *prefix_str;
+} test_node_t;
+
+struct event_loop *master;
+
+/*
+ * add_node
+ *
+ * Add the given prefix (passed in as a string) to the given table.
+ */
+static void add_node(struct route_table *table, const char *prefix_str)
+{
+ struct prefix_ipv4 p;
+ test_node_t *node;
+ struct route_node *rn;
+
+ assert(prefix_str);
+
+ if (str2prefix_ipv4(prefix_str, &p) <= 0) {
+ assert(0);
+ }
+
+ rn = route_node_get(table, (struct prefix *)&p);
+ if (rn->info) {
+ assert(0);
+ return;
+ }
+
+ node = malloc(sizeof(test_node_t));
+ assert(node);
+ node->prefix_str = strdup(prefix_str);
+ assert(node->prefix_str);
+ rn->info = node;
+}
+
+/*
+ * add_nodes
+ *
+ * Convenience function to add a bunch of nodes together.
+ *
+ * The arguments must be prefixes in string format, with a NULL as the
+ * last argument.
+ */
+static void add_nodes(struct route_table *table, ...)
+{
+ va_list arglist;
+ char *prefix;
+
+ va_start(arglist, table);
+
+ prefix = va_arg(arglist, char *);
+ while (prefix) {
+ add_node(table, prefix);
+ prefix = va_arg(arglist, char *);
+ }
+
+ va_end(arglist);
+}
+
+/*
+ * print_subtree
+ *
+ * Recursive function to print a route node and its children.
+ *
+ * @see print_table
+ */
+static void print_subtree(struct route_node *rn, const char *legend,
+ int indent_level)
+{
+ int i;
+
+ /*
+ * Print this node first.
+ */
+ for (i = 0; i < indent_level; i++) {
+ printf(" ");
+ }
+
+ printfrr("%s: %pFX", legend, &rn->p);
+ if (!rn->info) {
+ printf(" (internal)");
+ }
+ printf("\n");
+ if (rn->l_left) {
+ print_subtree(rn->l_left, "Left", indent_level + 1);
+ }
+ if (rn->l_right) {
+ print_subtree(rn->l_right, "Right", indent_level + 1);
+ }
+}
+
+/*
+ * print_table
+ *
+ * Function that prints out the internal structure of a route table.
+ */
+static void print_table(struct route_table *table)
+{
+ struct route_node *rn;
+
+ rn = table->top;
+
+ if (!rn) {
+ printf("<Empty Table>\n");
+ return;
+ }
+
+ print_subtree(rn, "Top", 0);
+}
+
+/*
+ * clear_table
+ *
+ * Remove all nodes from the given table.
+ */
+static void clear_table(struct route_table *table)
+{
+ route_table_iter_t iter;
+ struct route_node *rn;
+ test_node_t *node;
+
+ route_table_iter_init(&iter, table);
+
+ while ((rn = route_table_iter_next(&iter))) {
+ node = rn->info;
+ if (!node) {
+ continue;
+ }
+ rn->info = NULL;
+ route_unlock_node(rn);
+ free(node->prefix_str);
+ free(node);
+ }
+
+ route_table_iter_cleanup(&iter);
+
+ assert(table->top == NULL);
+}
+
+/*
+ * verify_next_by_iterating
+ *
+ * Iterate over the tree to make sure that the first prefix after
+ * target_pfx is the expected one. Note that target_pfx may not be
+ * present in the tree.
+ */
+static void verify_next_by_iterating(struct route_table *table,
+ struct prefix *target_pfx,
+ struct prefix *next_pfx)
+{
+ route_table_iter_t iter;
+ struct route_node *rn;
+
+ route_table_iter_init(&iter, table);
+ while ((rn = route_table_iter_next(&iter))) {
+ if (route_table_prefix_iter_cmp(&rn->p, target_pfx) > 0) {
+ assert(!prefix_cmp(&rn->p, next_pfx));
+ break;
+ }
+ }
+
+ if (!rn) {
+ assert(!next_pfx);
+ }
+
+ route_table_iter_cleanup(&iter);
+}
+
+/*
+ * verify_next
+ *
+ * Verifies that route_table_get_next() returns the expected result
+ * (result) for the prefix string 'target'.
+ */
+static void verify_next(struct route_table *table, const char *target,
+ const char *next)
+{
+ struct prefix_ipv4 target_pfx, next_pfx;
+ struct route_node *rn;
+ char result_buf[PREFIX2STR_BUFFER];
+
+ if (str2prefix_ipv4(target, &target_pfx) <= 0) {
+ assert(0);
+ }
+
+ rn = route_table_get_next(table, (struct prefix *)&target_pfx);
+ if (rn) {
+ prefix2str(&rn->p, result_buf, sizeof(result_buf));
+ } else {
+ snprintf(result_buf, sizeof(result_buf), "(Null)");
+ }
+
+ printf("\n");
+ print_table(table);
+ printf("Verifying successor of %s. Expected: %s, Result: %s\n", target,
+ next ? next : "(Null)", result_buf);
+
+ if (!rn) {
+ assert(!next);
+ verify_next_by_iterating(table, (struct prefix *)&target_pfx,
+ NULL);
+ return;
+ }
+
+ assert(next);
+
+ if (str2prefix_ipv4(next, &next_pfx) <= 0) {
+ assert(0);
+ }
+
+ if (prefix_cmp(&rn->p, (struct prefix *)&next_pfx)) {
+ assert(0);
+ }
+ route_unlock_node(rn);
+
+ verify_next_by_iterating(table, (struct prefix *)&target_pfx,
+ (struct prefix *)&next_pfx);
+}
+
+/*
+ * test_get_next
+ */
+static void test_get_next(void)
+{
+ struct route_table *table;
+
+ printf("\n\nTesting route_table_get_next()\n");
+ table = route_table_init();
+
+ /*
+ * Target exists in tree, but has no successor.
+ */
+ add_nodes(table, "1.0.1.0/24", NULL);
+ verify_next(table, "1.0.1.0/24", NULL);
+ clear_table(table);
+
+ /*
+ * Target exists in tree, and there is a node in its left subtree.
+ */
+ add_nodes(table, "1.0.1.0/24", "1.0.1.0/25", NULL);
+ verify_next(table, "1.0.1.0/24", "1.0.1.0/25");
+ clear_table(table);
+
+ /*
+ * Target exists in tree, and there is a node in its right subtree.
+ */
+ add_nodes(table, "1.0.1.0/24", "1.0.1.128/25", NULL);
+ verify_next(table, "1.0.1.0/24", "1.0.1.128/25");
+ clear_table(table);
+
+ /*
+ * Target exists in the tree, next node is outside subtree.
+ */
+ add_nodes(table, "1.0.1.0/24", "1.1.0.0/16", NULL);
+ verify_next(table, "1.0.1.0/24", "1.1.0.0/16");
+ clear_table(table);
+
+ /*
+ * The target node does not exist in the tree for all the test cases
+ * below this point.
+ */
+
+ /*
+ * There is no successor in the tree.
+ */
+ add_nodes(table, "1.0.0.0/16", NULL);
+ verify_next(table, "1.0.1.0/24", NULL);
+ clear_table(table);
+
+ /*
+ * There exists a node that would be in the target's left subtree.
+ */
+ add_nodes(table, "1.0.0.0/16", "1.0.1.0/25", NULL);
+ verify_next(table, "1.0.1.0/24", "1.0.1.0/25");
+ clear_table(table);
+
+ /*
+ * There exists a node would be in the target's right subtree.
+ */
+ add_nodes(table, "1.0.0.0/16", "1.0.1.128/25", NULL);
+ verify_next(table, "1.0.1.0/24", "1.0.1.128/25");
+ clear_table(table);
+
+ /*
+ * A search for the target reaches a node where there are no child
+ * nodes in the direction of the target (left), but the node has a
+ * right child.
+ */
+ add_nodes(table, "1.0.0.0/16", "1.0.128.0/17", NULL);
+ verify_next(table, "1.0.0.0/17", "1.0.128.0/17");
+ clear_table(table);
+
+ /*
+ * A search for the target reaches a node with no children. We have
+ * to go upwards in the tree to find a successor.
+ */
+ add_nodes(table, "1.0.0.0/16", "1.0.0.0/24", "1.0.1.0/24",
+ "1.0.128.0/17", NULL);
+ verify_next(table, "1.0.1.0/25", "1.0.128.0/17");
+ clear_table(table);
+
+ /*
+ * A search for the target reaches a node where neither the node nor
+ * the target prefix contain each other.
+ *
+ * In first case below the node succeeds the target.
+ *
+ * In the second case, the node comes before the target, so we have
+ * to go up the tree looking for a successor.
+ */
+ add_nodes(table, "1.0.0.0/16", "1.0.1.0/24", NULL);
+ verify_next(table, "1.0.0.0/24", "1.0.1.0/24");
+ clear_table(table);
+
+ add_nodes(table, "1.0.0.0/16", "1.0.0.0/24", "1.0.1.0/25",
+ "1.0.128.0/17", NULL);
+ verify_next(table, "1.0.1.128/25", "1.0.128.0/17");
+ clear_table(table);
+
+ route_table_finish(table);
+}
+
+/*
+ * verify_prefix_iter_cmp
+ */
+static void verify_prefix_iter_cmp(const char *p1, const char *p2,
+ int exp_result)
+{
+ struct prefix_ipv4 p1_pfx, p2_pfx;
+ int result;
+
+ if (str2prefix_ipv4(p1, &p1_pfx) <= 0) {
+ assert(0);
+ }
+
+ if (str2prefix_ipv4(p2, &p2_pfx) <= 0) {
+ assert(0);
+ }
+
+ result = route_table_prefix_iter_cmp((struct prefix *)&p1_pfx,
+ (struct prefix *)&p2_pfx);
+
+ printf("Verifying cmp(%s, %s) returns %d\n", p1, p2, exp_result);
+
+ assert(exp_result == result);
+
+ /*
+ * Also check the reverse comparison.
+ */
+ result = route_table_prefix_iter_cmp((struct prefix *)&p2_pfx,
+ (struct prefix *)&p1_pfx);
+
+ if (exp_result) {
+ exp_result = -exp_result;
+ }
+
+ printf("Verifying cmp(%s, %s) returns %d\n", p1, p2, exp_result);
+ assert(result == exp_result);
+}
+
+/*
+ * test_prefix_iter_cmp
+ *
+ * Tests comparison of prefixes according to order of iteration.
+ */
+static void test_prefix_iter_cmp(void)
+{
+ printf("\n\nTesting route_table_prefix_iter_cmp()\n");
+
+ verify_prefix_iter_cmp("1.0.0.0/8", "1.0.0.0/8", 0);
+
+ verify_prefix_iter_cmp("1.0.0.0/8", "1.0.0.0/16", -1);
+
+ verify_prefix_iter_cmp("1.0.0.0/16", "1.128.0.0/16", -1);
+}
+
+/*
+ * verify_iter_with_pause
+ *
+ * Iterates over a tree using two methods: 'normal' iteration, and an
+ * iterator that pauses at each node. Verifies that the two methods
+ * yield the same results.
+ */
+static void verify_iter_with_pause(struct route_table *table)
+{
+ unsigned long num_nodes;
+ struct route_node *rn, *iter_rn;
+ route_table_iter_t iter_space;
+ route_table_iter_t *iter = &iter_space;
+
+ route_table_iter_init(iter, table);
+ num_nodes = 0;
+
+ for (rn = route_top(table); rn; rn = route_next(rn)) {
+ num_nodes++;
+ route_table_iter_pause(iter);
+
+ assert(iter->current == NULL);
+ if (route_table_iter_started(iter)) {
+ assert(iter->state == RT_ITER_STATE_PAUSED);
+ } else {
+ assert(rn == table->top);
+ assert(iter->state == RT_ITER_STATE_INIT);
+ }
+
+ iter_rn = route_table_iter_next(iter);
+
+ /*
+ * Make sure both iterations return the same node.
+ */
+ assert(rn == iter_rn);
+ }
+
+ assert(num_nodes == route_table_count(table));
+
+ route_table_iter_pause(iter);
+ iter_rn = route_table_iter_next(iter);
+
+ assert(iter_rn == NULL);
+ assert(iter->state == RT_ITER_STATE_DONE);
+
+ assert(route_table_iter_next(iter) == NULL);
+ assert(iter->state == RT_ITER_STATE_DONE);
+
+ route_table_iter_cleanup(iter);
+
+ print_table(table);
+ printf("Verified pausing iteration on tree with %lu nodes\n",
+ num_nodes);
+}
+
+/*
+ * test_iter_pause
+ */
+static void test_iter_pause(void)
+{
+ struct route_table *table;
+ int i, num_prefixes;
+ const char *prefixes[] = {"1.0.1.0/24", "1.0.1.0/25", "1.0.1.128/25",
+ "1.0.2.0/24", "2.0.0.0/8"};
+
+ num_prefixes = array_size(prefixes);
+
+ printf("\n\nTesting that route_table_iter_pause() works as expected\n");
+ table = route_table_init();
+ for (i = 0; i < num_prefixes; i++) {
+ add_nodes(table, prefixes[i], NULL);
+ }
+
+ verify_iter_with_pause(table);
+
+ clear_table(table);
+ route_table_finish(table);
+}
+
+/*
+ * run_tests
+ */
+static void run_tests(void)
+{
+ test_prefix_iter_cmp();
+ test_get_next();
+ test_iter_pause();
+}
+
+/*
+ * main
+ */
+int main(void)
+{
+ run_tests();
+}
diff --git a/tests/lib/test_table.py b/tests/lib/test_table.py
new file mode 100644
index 0000000..ee1849f
--- /dev/null
+++ b/tests/lib/test_table.py
@@ -0,0 +1,12 @@
+import frrtest
+
+
+class TestTable(frrtest.TestMultiOut):
+ program = "./test_table"
+
+
+for i in range(6):
+ TestTable.onesimple("Verifying cmp")
+for i in range(11):
+ TestTable.onesimple("Verifying successor")
+TestTable.onesimple("Verified pausing")
diff --git a/tests/lib/test_timer_correctness.c b/tests/lib/test_timer_correctness.c
new file mode 100644
index 0000000..04c0516
--- /dev/null
+++ b/tests/lib/test_timer_correctness.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Test program to verify that scheduled timers are executed in the
+ * correct order.
+ *
+ * Copyright (C) 2013 by Open Source Routing.
+ * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of Quagga
+ */
+
+#include <zebra.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "memory.h"
+#include "prng.h"
+#include "frrevent.h"
+
+#define SCHEDULE_TIMERS 800
+#define REMOVE_TIMERS 200
+
+#define TIMESTR_LEN strlen("4294967296.999999")
+
+struct event_loop *master;
+
+static size_t log_buf_len;
+static size_t log_buf_pos;
+static char *log_buf;
+
+static size_t expected_buf_len;
+static size_t expected_buf_pos;
+static char *expected_buf;
+
+static struct prng *prng;
+
+static struct event **timers;
+
+static int timers_pending;
+
+static void terminate_test(void)
+{
+ int exit_code;
+
+ if (strcmp(log_buf, expected_buf)) {
+ fprintf(stderr,
+ "Expected output and received output differ.\n");
+ fprintf(stderr, "---Expected output: ---\n%s", expected_buf);
+ fprintf(stderr, "---Actual output: ---\n%s", log_buf);
+ exit_code = 1;
+ } else {
+ printf("Expected output and actual output match.\n");
+ exit_code = 0;
+ }
+
+ event_master_free(master);
+ XFREE(MTYPE_TMP, log_buf);
+ XFREE(MTYPE_TMP, expected_buf);
+ prng_free(prng);
+ XFREE(MTYPE_TMP, timers);
+
+ exit(exit_code);
+}
+
+static void timer_func(struct event *thread)
+{
+ int rv;
+
+ rv = snprintf(log_buf + log_buf_pos, log_buf_len - log_buf_pos, "%s\n",
+ (char *)thread->arg);
+ assert(rv >= 0);
+ log_buf_pos += rv;
+ assert(log_buf_pos < log_buf_len);
+ XFREE(MTYPE_TMP, thread->arg);
+
+ timers_pending--;
+ if (!timers_pending)
+ terminate_test();
+}
+
+static int cmp_timeval(const void *a, const void *b)
+{
+ const struct timeval *ta = *(struct timeval * const *)a;
+ const struct timeval *tb = *(struct timeval * const *)b;
+
+ if (timercmp(ta, tb, <))
+ return -1;
+ if (timercmp(ta, tb, >))
+ return 1;
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int i, j;
+ struct event t;
+ struct timeval **alarms;
+
+ master = event_master_create(NULL);
+
+ log_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1;
+ log_buf_pos = 0;
+ log_buf = XMALLOC(MTYPE_TMP, log_buf_len);
+
+ expected_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1;
+ expected_buf_pos = 0;
+ expected_buf = XMALLOC(MTYPE_TMP, expected_buf_len);
+
+ prng = prng_new(0);
+
+ timers = XCALLOC(MTYPE_TMP, SCHEDULE_TIMERS * sizeof(*timers));
+
+ for (i = 0; i < SCHEDULE_TIMERS; i++) {
+ long interval_msec;
+ int ret;
+ char *arg;
+
+ /* Schedule timers to expire in 0..5 seconds */
+ interval_msec = prng_rand(prng) % 5000;
+ arg = XMALLOC(MTYPE_TMP, TIMESTR_LEN + 1);
+ event_add_timer_msec(master, timer_func, arg, interval_msec,
+ &timers[i]);
+ ret = snprintf(arg, TIMESTR_LEN + 1, "%lld.%06lld",
+ (long long)timers[i]->u.sands.tv_sec,
+ (long long)timers[i]->u.sands.tv_usec);
+ assert(ret > 0);
+ assert((size_t)ret < TIMESTR_LEN + 1);
+ timers_pending++;
+ }
+
+ for (i = 0; i < REMOVE_TIMERS; i++) {
+ int index;
+
+ index = prng_rand(prng) % SCHEDULE_TIMERS;
+ if (!timers[index])
+ continue;
+
+ XFREE(MTYPE_TMP, timers[index]->arg);
+ event_cancel(&timers[index]);
+ timers_pending--;
+ }
+
+ /* We create an array of pointers to the alarm times and sort
+ * that array. That sorted array is used to generate a string
+ * representing the expected "output" of the timers when they
+ * are run. */
+ j = 0;
+ alarms = XCALLOC(MTYPE_TMP, timers_pending * sizeof(*alarms));
+ for (i = 0; i < SCHEDULE_TIMERS; i++) {
+ if (!timers[i])
+ continue;
+ alarms[j++] = &timers[i]->u.sands;
+ }
+ qsort(alarms, j, sizeof(*alarms), cmp_timeval);
+ for (i = 0; i < j; i++) {
+ int ret;
+
+ ret = snprintf(expected_buf + expected_buf_pos,
+ expected_buf_len - expected_buf_pos,
+ "%lld.%06lld\n", (long long)alarms[i]->tv_sec,
+ (long long)alarms[i]->tv_usec);
+ assert(ret > 0);
+ expected_buf_pos += ret;
+ assert(expected_buf_pos < expected_buf_len);
+ }
+ XFREE(MTYPE_TMP, alarms);
+
+ while (event_fetch(master, &t))
+ event_call(&t);
+
+ return 0;
+}
diff --git a/tests/lib/test_timer_correctness.py b/tests/lib/test_timer_correctness.py
new file mode 100644
index 0000000..71f45f9
--- /dev/null
+++ b/tests/lib/test_timer_correctness.py
@@ -0,0 +1,8 @@
+import frrtest
+
+
+class TestTimerCorrectness(frrtest.TestMultiOut):
+ program = "./test_timer_correctness"
+
+
+TestTimerCorrectness.onesimple("Expected output and actual output match.")
diff --git a/tests/lib/test_timer_performance.c b/tests/lib/test_timer_performance.c
new file mode 100644
index 0000000..3ace076
--- /dev/null
+++ b/tests/lib/test_timer_performance.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Test program which measures the time it takes to schedule and
+ * remove timers.
+ *
+ * Copyright (C) 2013 by Open Source Routing.
+ * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of Quagga
+ */
+
+#include <zebra.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "frrevent.h"
+#include "prng.h"
+
+#define SCHEDULE_TIMERS 1000000
+#define REMOVE_TIMERS 500000
+
+struct event_loop *master;
+
+static void dummy_func(struct event *thread)
+{
+}
+
+int main(int argc, char **argv)
+{
+ struct prng *prng;
+ int i;
+ struct event **timers;
+ struct timeval tv_start, tv_lap, tv_stop;
+ unsigned long t_schedule, t_remove;
+
+ master = event_master_create(NULL);
+ prng = prng_new(0);
+ timers = calloc(SCHEDULE_TIMERS, sizeof(*timers));
+
+ /* create thread structures so they won't be allocated during the
+ * time measurement */
+ for (i = 0; i < SCHEDULE_TIMERS; i++) {
+ event_add_timer_msec(master, dummy_func, NULL, 0, &timers[i]);
+ }
+ for (i = 0; i < SCHEDULE_TIMERS; i++)
+ event_cancel(&timers[i]);
+
+ monotime(&tv_start);
+
+ for (i = 0; i < SCHEDULE_TIMERS; i++) {
+ long interval_msec;
+
+ interval_msec = prng_rand(prng) % (100 * SCHEDULE_TIMERS);
+ event_add_timer_msec(master, dummy_func, NULL, interval_msec,
+ &timers[i]);
+ }
+
+ monotime(&tv_lap);
+
+ for (i = 0; i < REMOVE_TIMERS; i++) {
+ int index;
+
+ index = prng_rand(prng) % SCHEDULE_TIMERS;
+ event_cancel(&timers[index]);
+ }
+
+ monotime(&tv_stop);
+
+ t_schedule = 1000 * (tv_lap.tv_sec - tv_start.tv_sec);
+ t_schedule += (tv_lap.tv_usec - tv_start.tv_usec) / 1000;
+
+ t_remove = 1000 * (tv_stop.tv_sec - tv_lap.tv_sec);
+ t_remove += (tv_stop.tv_usec - tv_lap.tv_usec) / 1000;
+
+ printf("Scheduling %d random timers took %lu.%03lu seconds.\n",
+ SCHEDULE_TIMERS, t_schedule / 1000, t_schedule % 1000);
+ printf("Removing %d random timers took %lu.%03lu seconds.\n",
+ REMOVE_TIMERS, t_remove / 1000, t_remove % 1000);
+ fflush(stdout);
+
+ free(timers);
+ event_master_free(master);
+ prng_free(prng);
+ return 0;
+}
diff --git a/tests/lib/test_ttable.c b/tests/lib/test_ttable.c
new file mode 100644
index 0000000..562ddf9
--- /dev/null
+++ b/tests/lib/test_ttable.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ASCII table generator.
+ * Copyright (C) 2017 Cumulus Networks
+ * Quentin Young
+ */
+#include <zebra.h>
+#include <termtable.h>
+#include <memory.h>
+
+int main(int argc, char **argv)
+{
+ char *table;
+
+ struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_ASCII]);
+
+ /* test printf compatibility and dimension counters */
+ ttable_add_row(tt, "%s|%s|%s", "Column 1", "Column 2", "Column 3");
+ assert(tt->ncols == 3);
+ assert(tt->nrows == 1);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* add new row with 1 column, assert that it is not added */
+ assert(ttable_add_row(tt, "%s", "Garbage") == NULL);
+ assert(tt->ncols == 3);
+ assert(tt->nrows == 1);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* add new row, assert that it is added */
+ assert(ttable_add_row(tt, "%s|%s|%s", "a", "b", "c"));
+ assert(tt->ncols == 3);
+ assert(tt->nrows == 2);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* add empty row, assert that it is added */
+ assert(ttable_add_row(tt, "||"));
+ assert(tt->ncols == 3);
+ assert(tt->nrows == 3);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* delete 1st row, assert that it is removed */
+ ttable_del_row(tt, 0);
+ assert(tt->ncols == 3);
+ assert(tt->nrows == 2);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* delete last row, assert that it is removed */
+ ttable_del_row(tt, 0);
+ assert(tt->ncols == 3);
+ assert(tt->nrows == 1);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* delete the remaining row, check dumping an empty table */
+ ttable_del_row(tt, 0);
+ assert(tt->ncols == 0);
+ assert(tt->nrows == 0);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* add new row */
+ ttable_add_row(tt, "%s|%s||%s|%9d", "slick", "black", "triple", 1337);
+ assert(tt->ncols == 5);
+ assert(tt->nrows == 1);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* add bigger row */
+ ttable_add_row(tt, "%s|%s||%s|%s",
+ "nebula dusk session streets twilight pioneer beats yeah",
+ "prarie dog", "cornmeal", ":O -*_-*");
+ assert(tt->ncols == 5);
+ assert(tt->nrows == 2);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* insert new row at beginning */
+ ttable_insert_row(tt, 0, "%s|%s||%d|%lf", "converting", "vegetarians",
+ 2, 2015.0);
+ assert(tt->ncols == 5);
+ assert(tt->nrows == 3);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* insert new row at end */
+ ttable_insert_row(tt, tt->nrows - 1, "%s|%s||%d|%ld", "converting",
+ "vegetarians", 1, 2003L);
+ assert(tt->ncols == 5);
+ assert(tt->nrows == 4);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* insert new row at middle */
+ ttable_insert_row(tt, 1, "%s|%s||%s|%ld", "she", "pioneer", "aki", 1l);
+ assert(tt->ncols == 5);
+ assert(tt->nrows == 5);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* set alignment */
+ ttable_align(tt, 0, 1, 2, 2, LEFT);
+ assert(tt->ncols == 5);
+ assert(tt->nrows == 5);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ ttable_align(tt, 0, 1, 5, 1, RIGHT);
+ assert(tt->ncols == 5);
+ assert(tt->nrows == 5);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* set padding */
+ ttable_pad(tt, 0, 1, 1, 1, RIGHT, 2);
+ assert(tt->ncols == 5);
+ assert(tt->nrows == 5);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ ttable_pad(tt, 0, 0, 5, 4, LEFT, 2);
+ assert(tt->ncols == 5);
+ assert(tt->nrows == 5);
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* restyle */
+ tt->style.cell.border.bottom_on = false;
+ tt->style.cell.border.top_on = false;
+ tt->style.cell.border.right_on = false;
+ tt->style.cell.border.left_on = false;
+ ttable_restyle(tt);
+
+ /* top & double bottom border for top row */
+ ttable_rowseps(tt, 0, BOTTOM, true, '-');
+ ttable_rowseps(tt, 1, TOP, true, '-');
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* column separators for leftmost column */
+ ttable_colseps(tt, 0, RIGHT, true, '|');
+ table = ttable_dump(tt, "\n");
+ fprintf(stdout, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ /* delete table */
+ ttable_del(tt);
+}
diff --git a/tests/lib/test_ttable.py b/tests/lib/test_ttable.py
new file mode 100644
index 0000000..9151181
--- /dev/null
+++ b/tests/lib/test_ttable.py
@@ -0,0 +1,5 @@
+import frrtest
+
+
+class TestTTable(frrtest.TestRefOut):
+ program = "./test_ttable"
diff --git a/tests/lib/test_ttable.refout b/tests/lib/test_ttable.refout
new file mode 100644
index 0000000..fb59c0f
--- /dev/null
+++ b/tests/lib/test_ttable.refout
@@ -0,0 +1,143 @@
+ |--------------------------------|
+ | Column 1 | Column 2 | Column 3 |
+ |--------------------------------|
+
+ |--------------------------------|
+ | Column 1 | Column 2 | Column 3 |
+ |--------------------------------|
+
+ |--------------------------------|
+ | Column 1 | Column 2 | Column 3 |
+ |----------+----------+----------|
+ | a | b | c |
+ |--------------------------------|
+
+ |--------------------------------|
+ | Column 1 | Column 2 | Column 3 |
+ |----------+----------+----------|
+ | a | b | c |
+ |----------+----------+----------|
+ | | | |
+ |--------------------------------|
+
+ |-----------|
+ | a | b | c |
+ |---+---+---|
+ | | | |
+ |-----------|
+
+ |--------|
+ | | | |
+ |--------|
+
+ ||
+ ||
+
+ |---------------------------------------|
+ | slick | black | | triple | 1337 |
+ |---------------------------------------|
+
+ |------------------------------------------------------------------------------------------------|
+ | slick | black | | triple | 1337 |
+ |---------------------------------------------------------+------------+--+----------+-----------|
+ | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
+ |------------------------------------------------------------------------------------------------|
+
+ |---------------------------------------------------------------------------------------------------|
+ | converting | vegetarians | | 2 | 2015.000000 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | slick | black | | triple | 1337 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
+ |---------------------------------------------------------------------------------------------------|
+
+ |---------------------------------------------------------------------------------------------------|
+ | converting | vegetarians | | 2 | 2015.000000 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | slick | black | | triple | 1337 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | converting | vegetarians | | 1 | 2003 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
+ |---------------------------------------------------------------------------------------------------|
+
+ |---------------------------------------------------------------------------------------------------|
+ | converting | vegetarians | | 2 | 2015.000000 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | she | pioneer | | aki | 1 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | slick | black | | triple | 1337 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | converting | vegetarians | | 1 | 2003 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
+ |---------------------------------------------------------------------------------------------------|
+
+ |---------------------------------------------------------------------------------------------------|
+ | converting | vegetarians | | 2 | 2015.000000 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | she | pioneer | | aki | 1 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | slick | black | | triple | 1337 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | converting | vegetarians | | 1 | 2003 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
+ |---------------------------------------------------------------------------------------------------|
+
+ |---------------------------------------------------------------------------------------------------|
+ | converting | vegetarians | | 2 | 2015.000000 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | she | pioneer | | aki | 1 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | slick | black | | triple | 1337 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | converting | vegetarians | | 1 | 2003 |
+ |---------------------------------------------------------+-------------+--+----------+-------------|
+ | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
+ |---------------------------------------------------------------------------------------------------|
+
+ |----------------------------------------------------------------------------------------------------|
+ | converting | vegetarians | | 2 | 2015.000000 |
+ |---------------------------------------------------------+--------------+--+----------+-------------|
+ | she | pioneer | | aki | 1 |
+ |---------------------------------------------------------+--------------+--+----------+-------------|
+ | slick | black | | triple | 1337 |
+ |---------------------------------------------------------+--------------+--+----------+-------------|
+ | converting | vegetarians | | 1 | 2003 |
+ |---------------------------------------------------------+--------------+--+----------+-------------|
+ | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
+ |----------------------------------------------------------------------------------------------------|
+
+ |--------------------------------------------------------------------------------------------------------|
+ | converting | vegetarians | | 2 | 2015.000000 |
+ |----------------------------------------------------------+---------------+---+-----------+-------------|
+ | she | pioneer | | aki | 1 |
+ |----------------------------------------------------------+---------------+---+-----------+-------------|
+ | slick | black | | triple | 1337 |
+ |----------------------------------------------------------+---------------+---+-----------+-------------|
+ | converting | vegetarians | | 1 | 2003 |
+ |----------------------------------------------------------+---------------+---+-----------+-------------|
+ | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
+ |--------------------------------------------------------------------------------------------------------|
+
+ |-----------------------------------------------------------------------------------------------|
+ | converting vegetarians 2 2015.000000 |
+ |-----------------------------------------------------------------------------------------------|
+ |-----------------------------------------------------------------------------------------------|
+ | she pioneer aki 1 |
+ | slick black triple 1337 |
+ | converting vegetarians 1 2003 |
+ | nebula dusk session streets twilight pioneer beats yeah prarie dog cornmeal :O -*_-* |
+ |-----------------------------------------------------------------------------------------------|
+
+ |------------------------------------------------------------------------------------------------|
+ | converting | vegetarians 2 2015.000000 |
+ |---------------------------------------------------------+--------------------------------------|
+ |---------------------------------------------------------+--------------------------------------|
+ | she | pioneer aki 1 |
+ | slick | black triple 1337 |
+ | converting | vegetarians 1 2003 |
+ | nebula dusk session streets twilight pioneer beats yeah | prarie dog cornmeal :O -*_-* |
+ |------------------------------------------------------------------------------------------------|
+
diff --git a/tests/lib/test_typelist.c b/tests/lib/test_typelist.c
new file mode 100644
index 0000000..070a304
--- /dev/null
+++ b/tests/lib/test_typelist.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <arpa/inet.h>
+
+#define WNO_ATOMLIST_UNSAFE_FIND
+
+#include "typesafe.h"
+#include "atomlist.h"
+#include "memory.h"
+#include "monotime.h"
+#include "jhash.h"
+#include "sha256.h"
+#include "printfrr.h"
+
+#include "tests/helpers/c/prng.h"
+
+/* note: these macros are layered 2-deep because that makes the C
+ * preprocessor expand the "type" argument. Otherwise, you get
+ * "PREDECL_type" instead of "PREDECL_LIST"
+ */
+#define _concat(a, b) a ## b
+#define concat(a, b) _concat(a, b)
+#define _str(x) #x
+#define str(x) _str(x)
+
+#define _PREDECL(type, ...) PREDECL_##type(__VA_ARGS__)
+#define PREDECL(type, ...) _PREDECL(type, __VA_ARGS__)
+#define _DECLARE(type, ...) DECLARE_##type(__VA_ARGS__)
+#define DECLARE(type, ...) _DECLARE(type, __VA_ARGS__)
+
+#define T_SORTED (1 << 0)
+#define T_UNIQ (1 << 1)
+#define T_HASH (1 << 2)
+#define T_HEAP (1 << 3)
+#define T_ATOMIC (1 << 4)
+#define T_REVERSE (1 << 5)
+
+#define _T_LIST (0)
+#define _T_DLIST (0 | T_REVERSE)
+#define _T_ATOMLIST (0 | T_ATOMIC)
+#define _T_HEAP (T_SORTED | T_HEAP)
+#define _T_SORTLIST_UNIQ (T_SORTED | T_UNIQ)
+#define _T_SORTLIST_NONUNIQ (T_SORTED)
+#define _T_HASH (T_SORTED | T_UNIQ | T_HASH)
+#define _T_SKIPLIST_UNIQ (T_SORTED | T_UNIQ)
+#define _T_SKIPLIST_NONUNIQ (T_SORTED)
+#define _T_RBTREE_UNIQ (T_SORTED | T_UNIQ | T_REVERSE)
+#define _T_RBTREE_NONUNIQ (T_SORTED | T_REVERSE)
+#define _T_ATOMSORT_UNIQ (T_SORTED | T_UNIQ | T_ATOMIC)
+#define _T_ATOMSORT_NONUNIQ (T_SORTED | T_ATOMIC)
+
+#define _T_TYPE(type) _T_##type
+#define IS_SORTED(type) (_T_TYPE(type) & T_SORTED)
+#define IS_UNIQ(type) (_T_TYPE(type) & T_UNIQ)
+#define IS_HASH(type) (_T_TYPE(type) & T_HASH)
+#define IS_HEAP(type) (_T_TYPE(type) & T_HEAP)
+#define IS_ATOMIC(type) (_T_TYPE(type) & T_ATOMIC)
+#define IS_REVERSE(type) (_T_TYPE(type) & T_REVERSE)
+
+static struct timeval ref, ref0;
+
+static void ts_start(void)
+{
+ monotime(&ref0);
+ monotime(&ref);
+}
+static void ts_ref(const char *text)
+{
+ int64_t us;
+ us = monotime_since(&ref, NULL);
+ printfrr("%7"PRId64"us %s\n", us, text);
+ monotime(&ref);
+}
+static void ts_end(void)
+{
+ int64_t us;
+ us = monotime_since(&ref0, NULL);
+ printfrr("%7"PRId64"us total\n", us);
+}
+
+#define TYPE LIST
+#include "test_typelist.h"
+
+#define TYPE DLIST
+#include "test_typelist.h"
+
+#define TYPE ATOMLIST
+#include "test_typelist.h"
+
+#define TYPE HEAP
+#include "test_typelist.h"
+
+#define TYPE SORTLIST_UNIQ
+#include "test_typelist.h"
+
+#define TYPE SORTLIST_NONUNIQ
+#include "test_typelist.h"
+
+#define TYPE HASH
+#include "test_typelist.h"
+
+#define TYPE HASH_collisions
+#define REALTYPE HASH
+#define SHITTY_HASH
+#include "test_typelist.h"
+#undef SHITTY_HASH
+
+#define TYPE SKIPLIST_UNIQ
+#include "test_typelist.h"
+
+#define TYPE SKIPLIST_NONUNIQ
+#include "test_typelist.h"
+
+#define TYPE RBTREE_UNIQ
+#include "test_typelist.h"
+
+#define TYPE RBTREE_NONUNIQ
+#include "test_typelist.h"
+
+#define TYPE ATOMSORT_UNIQ
+#include "test_typelist.h"
+
+#define TYPE ATOMSORT_NONUNIQ
+#include "test_typelist.h"
+
+int main(int argc, char **argv)
+{
+ srandom(1);
+
+ test_LIST();
+ test_DLIST();
+ test_ATOMLIST();
+ test_HEAP();
+ test_SORTLIST_UNIQ();
+ test_SORTLIST_NONUNIQ();
+ test_HASH();
+ test_HASH_collisions();
+ test_SKIPLIST_UNIQ();
+ test_SKIPLIST_NONUNIQ();
+ test_RBTREE_UNIQ();
+ test_RBTREE_NONUNIQ();
+ test_ATOMSORT_UNIQ();
+ test_ATOMSORT_NONUNIQ();
+
+ log_memstats_stderr("test: ");
+ return 0;
+}
diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h
new file mode 100644
index 0000000..80c4005
--- /dev/null
+++ b/tests/lib/test_typelist.h
@@ -0,0 +1,822 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2019 David Lamparter, for NetDEF, Inc.
+ */
+
+/* C++ called, they want their templates back */
+#define item concat(item_, TYPE)
+#define itm concat(itm_, TYPE)
+#define itmswap concat(itmswap_, TYPE)
+#define head concat(head_, TYPE)
+#define list concat(TYPE, )
+#define list_head concat(TYPE, _head)
+#define list_item concat(TYPE, _item)
+#define list_cmp concat(TYPE, _cmp)
+#define list_hash concat(TYPE, _hash)
+#define list_init concat(TYPE, _init)
+#define list_fini concat(TYPE, _fini)
+#define list_const_first concat(TYPE, _const_first)
+#define list_first concat(TYPE, _first)
+#define list_const_next concat(TYPE, _const_next)
+#define list_next concat(TYPE, _next)
+#define list_next_safe concat(TYPE, _next_safe)
+#define list_const_last concat(TYPE, _const_last)
+#define list_last concat(TYPE, _last)
+#define list_const_prev concat(TYPE, _const_prev)
+#define list_prev concat(TYPE, _prev)
+#define list_prev_safe concat(TYPE, _prev_safe)
+#define list_count concat(TYPE, _count)
+#define list_add concat(TYPE, _add)
+#define list_add_head concat(TYPE, _add_head)
+#define list_add_tail concat(TYPE, _add_tail)
+#define list_add_after concat(TYPE, _add_after)
+#define list_find concat(TYPE, _find)
+#define list_find_lt concat(TYPE, _find_lt)
+#define list_find_gteq concat(TYPE, _find_gteq)
+#define list_member concat(TYPE, _member)
+#define list_anywhere concat(TYPE, _anywhere)
+#define list_del concat(TYPE, _del)
+#define list_pop concat(TYPE, _pop)
+#define list_swap_all concat(TYPE, _swap_all)
+
+#define ts_hash_head concat(ts_hash_head_, TYPE)
+
+#ifndef REALTYPE
+#define REALTYPE TYPE
+#endif
+
+PREDECL(REALTYPE, list);
+struct item {
+ uint64_t val;
+ struct list_item itm;
+ int scratchpad;
+};
+
+#if IS_SORTED(REALTYPE)
+static int list_cmp(const struct item *a, const struct item *b);
+
+#if IS_HASH(REALTYPE)
+static uint32_t list_hash(const struct item *a);
+DECLARE(REALTYPE, list, struct item, itm, list_cmp, list_hash);
+
+static uint32_t list_hash(const struct item *a)
+{
+#ifdef SHITTY_HASH
+ /* crappy hash to get some hash collisions */
+ return (a->val & 0xFF) ^ (a->val << 29) ^ 0x55AA0000U;
+#else
+ return jhash_1word(a->val, 0xdeadbeef);
+#endif
+}
+
+#else
+DECLARE(REALTYPE, list, struct item, itm, list_cmp);
+#endif
+
+static int list_cmp(const struct item *a, const struct item *b)
+{
+ if (a->val > b->val)
+ return 1;
+ if (a->val < b->val)
+ return -1;
+ return 0;
+}
+
+#else /* !IS_SORTED */
+DECLARE(REALTYPE, list, struct item, itm);
+#endif
+
+#define NITEM 10000
+#define NITEM_SWAP 100 /* other container for swap */
+struct item itm[NITEM], itmswap[NITEM_SWAP];
+static struct list_head head = concat(INIT_, REALTYPE)(head);
+
+static void ts_hash_head(struct list_head *h, const char *text,
+ const char *expect)
+{
+ int64_t us = monotime_since(&ref, NULL);
+ SHA256_CTX ctx;
+ struct item *item;
+ unsigned i = 0;
+ uint8_t hash[32];
+ char hashtext[65];
+ uint32_t swap_count, count;
+
+ count = list_count(h);
+ swap_count = htonl(count);
+
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, &swap_count, sizeof(swap_count));
+
+ frr_each (list, h, item) {
+ struct {
+ uint32_t val_upper, val_lower, index;
+ } hashitem = {
+ htonl(item->val >> 32),
+ htonl(item->val & 0xFFFFFFFFULL),
+ htonl(i),
+ };
+ SHA256_Update(&ctx, &hashitem, sizeof(hashitem));
+ i++;
+ assert(i <= count);
+ }
+ SHA256_Final(hash, &ctx);
+
+ for (i = 0; i < sizeof(hash); i++)
+ sprintf(hashtext + i * 2, "%02x", hash[i]);
+
+ printfrr("%7"PRId64"us %-25s %s%s\n", us, text,
+ expect ? " " : "*", hashtext);
+ if (expect && strcmp(expect, hashtext)) {
+ printfrr("%-21s %s\n", "EXPECTED:", expect);
+ assert(0);
+ }
+ monotime(&ref);
+}
+/* hashes will have different item ordering */
+#if IS_HASH(REALTYPE) || IS_HEAP(REALTYPE)
+#define ts_hash(pos, csum) ts_hash_head(&head, pos, NULL)
+#define ts_hashx(pos, csum) ts_hash_head(&head, pos, NULL)
+#define ts_hash_headx(head, pos, csum) ts_hash_head(head, pos, NULL)
+#else
+#define ts_hash(pos, csum) ts_hash_head(&head, pos, csum)
+#define ts_hashx(pos, csum) ts_hash_head(&head, pos, csum)
+#define ts_hash_headx(head, pos, csum) ts_hash_head(head, pos, csum)
+#endif
+
+static void concat(test_, TYPE)(void)
+{
+ size_t i, j, k, l;
+ struct prng *prng;
+ struct prng *prng_swap __attribute__((unused));
+ struct item *item, *prev __attribute__((unused));
+ struct item dummy __attribute__((unused));
+
+ memset(itm, 0, sizeof(itm));
+ for (i = 0; i < NITEM; i++)
+ itm[i].val = i;
+
+ memset(itmswap, 0, sizeof(itmswap));
+ for (i = 0; i < NITEM_SWAP; i++)
+ itmswap[i].val = i;
+
+ printfrr("%s start\n", str(TYPE));
+ ts_start();
+
+ list_init(&head);
+ assert(list_first(&head) == NULL);
+#if IS_REVERSE(REALTYPE)
+ assert(list_last(&head) == NULL);
+#endif
+
+ ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
+
+#if !IS_ATOMIC(REALTYPE)
+ assert(!list_member(&head, &itm[0]));
+ assert(!list_member(&head, &itm[1]));
+#endif
+
+#if IS_SORTED(REALTYPE)
+ prng = prng_new(0);
+ k = 0;
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 0) {
+ list_add(&head, &itm[j]);
+ itm[j].scratchpad = 1;
+ k++;
+ }
+#if !IS_HEAP(REALTYPE)
+ else
+ assert(list_add(&head, &itm[j]) == &itm[j]);
+#endif
+ }
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+ ts_hashx("fill", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838");
+
+#if !IS_ATOMIC(REALTYPE)
+ struct list_head other;
+
+ list_init(&other);
+ list_swap_all(&head, &other);
+
+ assert(list_count(&head) == 0);
+ assert(!list_first(&head));
+ assert(list_count(&other) == k);
+ assert(list_first(&other) != NULL);
+#if IS_REVERSE(REALTYPE)
+ assert(!list_last(&head));
+ assert(list_last(&other) != NULL);
+#endif
+ ts_hash_headx(
+ &other, "swap1",
+ "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838");
+
+ prng_swap = prng_new(0x1234dead);
+ l = 0;
+ for (i = 0; i < NITEM_SWAP; i++) {
+ j = prng_rand(prng_swap) % NITEM_SWAP;
+ if (itmswap[j].scratchpad == 0) {
+ list_add(&head, &itmswap[j]);
+ itmswap[j].scratchpad = 1;
+ l++;
+ }
+#if !IS_HEAP(REALTYPE)
+ else {
+ struct item *rv = list_add(&head, &itmswap[j]);
+ assert(rv == &itmswap[j]);
+ }
+#endif
+ }
+ assert(list_count(&head) == l);
+ assert(list_first(&head) != NULL);
+ ts_hash_headx(
+ &head, "swap-fill",
+ "26df437174051cf305d1bbb62d779ee450ca764167a1e7a94be1aece420008e6");
+
+ list_swap_all(&head, &other);
+
+ assert(list_count(&other) == l);
+ assert(list_first(&other));
+ ts_hash_headx(
+ &other, "swap2a",
+ "26df437174051cf305d1bbb62d779ee450ca764167a1e7a94be1aece420008e6");
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+ ts_hash_headx(
+ &head, "swap2b",
+ "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838");
+
+ while (list_pop(&other))
+ ;
+ list_fini(&other);
+ prng_free(prng_swap);
+
+ ts_ref("swap-cleanup");
+#endif /* !IS_ATOMIC */
+
+ k = 0;
+
+#if IS_ATOMIC(REALTYPE)
+ struct list_head *chead = &head;
+ struct item *citem, *cprev = NULL;
+
+ frr_each(list, chead, citem) {
+#else
+ const struct list_head *chead = &head;
+ const struct item *citem, *cprev = NULL;
+
+ frr_each(list_const, chead, citem) {
+#endif
+
+#if IS_HASH(REALTYPE) || IS_HEAP(REALTYPE)
+ /* hash table doesn't give sorting */
+ (void)cprev;
+#else
+ assert(!cprev || cprev->val < citem->val);
+#if IS_REVERSE(REALTYPE)
+ assert(list_const_prev(chead, citem) == cprev);
+#endif
+#endif
+ cprev = citem;
+ k++;
+ }
+ assert(list_count(chead) == k);
+#if IS_REVERSE(REALTYPE)
+ assert(cprev == list_const_last(chead));
+#endif
+ ts_ref("walk");
+
+#if IS_REVERSE(REALTYPE) && !IS_HASH(REALTYPE) && !IS_HEAP(REALTYPE)
+ cprev = NULL;
+ k = 0;
+
+ frr_rev_each(list_const, chead, citem) {
+ assert(!cprev || cprev->val > citem->val);
+ assert(list_const_next(chead, citem) == cprev);
+
+ cprev = citem;
+ k++;
+ }
+ assert(list_count(chead) == k);
+ assert(cprev == list_const_first(chead));
+
+ ts_ref("reverse-walk");
+#endif
+
+#if IS_UNIQ(REALTYPE)
+ prng_free(prng);
+ prng = prng_new(0);
+
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ dummy.val = j;
+ assert(list_find(&head, &dummy) == &itm[j]);
+ }
+ ts_ref("find");
+
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ memset(&dummy, 0, sizeof(dummy));
+ dummy.val = j;
+ if (itm[j].scratchpad)
+ assert(list_add(&head, &dummy) == &itm[j]);
+ else {
+ assert(list_add(&head, &dummy) == NULL);
+ assert(list_del(&head, &dummy) != NULL);
+ }
+ }
+ ts_hashx("add-dup", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838");
+
+#elif IS_HEAP(REALTYPE)
+ /* heap - partially sorted. */
+ prev = NULL;
+ l = k / 4;
+ for (i = 0; i < l; i++) {
+ item = list_pop(&head);
+ if (prev)
+ assert(prev->val < item->val);
+ item->scratchpad = 0;
+ k--;
+ prev = item;
+ }
+ ts_hash("pop#1", NULL);
+
+ for (i = 0; i < NITEM; i++)
+ assertf(list_member(&head, &itm[i]) == itm[i].scratchpad,
+ "%zu should:%d is:%d", i, itm[i].scratchpad,
+ list_member(&head, &itm[i]));
+ ts_hash("member", NULL);
+
+ l = k / 2;
+ for (; i < l; i++) {
+ item = list_pop(&head);
+ if (prev)
+ assert(prev->val < item->val);
+ item->scratchpad = 0;
+ k--;
+ prev = item;
+ }
+ ts_hash("pop#2", NULL);
+
+#else /* !IS_UNIQ(REALTYPE) && !IS_HEAP(REALTYPE) */
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ memset(&dummy, 0, sizeof(dummy));
+ dummy.val = j;
+
+ list_add(&head, &dummy);
+ if (itm[j].scratchpad) {
+ struct item *lt, *gteq, dummy2;
+
+ assert(list_next(&head, &itm[j]) == &dummy ||
+ list_next(&head, &dummy) == &itm[j]);
+
+ memset(&dummy2, 0, sizeof(dummy));
+ dummy2.val = j;
+ lt = list_find_lt(&head, &dummy2);
+ gteq = list_find_gteq(&head, &dummy2);
+
+ assert(gteq == &itm[j] || gteq == &dummy);
+ if (lt)
+ assert(list_next(&head, lt) == &itm[j] ||
+ list_next(&head, lt) == &dummy);
+ else
+ assert(list_first(&head) == &itm[j] ||
+ list_first(&head) == &dummy);
+ } else if (list_next(&head, &dummy))
+ assert(list_next(&head, &dummy)->val > j);
+ assert(list_del(&head, &dummy) != NULL);
+ }
+ ts_hash("add-dup+find_{lt,gteq}", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838");
+#endif
+#if !IS_HASH(REALTYPE) && !IS_HEAP(REALTYPE)
+ prng_free(prng);
+ prng = prng_new(123456);
+
+ l = 0;
+ for (i = 0; i < NITEM; i++) {
+ struct item *lt, *gteq, *tmp;
+
+ j = prng_rand(prng) % NITEM;
+ dummy.val = j;
+
+ lt = list_find_lt(&head, &dummy);
+ gteq = list_find_gteq(&head, &dummy);
+
+ if (lt) {
+ assert(lt->val < j);
+ tmp = list_next(&head, lt);
+ assert(tmp == gteq);
+ assert(!tmp || tmp->val >= j);
+ } else
+ assert(gteq == list_first(&head));
+
+ if (gteq)
+ assert(gteq->val >= j);
+ }
+ ts_ref("find_{lt,gteq}");
+#endif /* !IS_HASH */
+
+ prng_free(prng);
+ prng = prng_new(0);
+
+ l = 0;
+ for (i = 0; i < NITEM; i++) {
+ (void)prng_rand(prng);
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 1) {
+ assert(list_del(&head, &itm[j]) != NULL);
+ itm[j].scratchpad = 0;
+ l++;
+ }
+ }
+ assert(l + list_count(&head) == k);
+ ts_hashx("del", "cb2e5d80f08a803ef7b56c15e981b681adcea214bebc2f55e12e0bfb242b07ca");
+
+#if !IS_ATOMIC(REALTYPE)
+ for (i = 0; i < NITEM; i++)
+ assertf(list_member(&head, &itm[i]) == itm[i].scratchpad,
+ "%zu should:%d is:%d", i, itm[i].scratchpad,
+ list_member(&head, &itm[i]));
+ ts_hashx("member", "cb2e5d80f08a803ef7b56c15e981b681adcea214bebc2f55e12e0bfb242b07ca");
+#endif
+
+ frr_each_safe(list, &head, item) {
+ assert(item->scratchpad != 0);
+
+ if (item->val & 1) {
+ assert(list_del(&head, item) != NULL);
+ item->scratchpad = 0;
+ l++;
+ }
+ }
+ assert(l + list_count(&head) == k);
+ ts_hashx("frr_each_safe+del", "e0beb71dd963a75af05b722b8e71b61b304587d860c8accdc4349067542b86bb");
+
+#else /* !IS_SORTED */
+ prng = prng_new(0);
+ k = 0;
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 0) {
+ list_add_tail(&head, &itm[j]);
+ itm[j].scratchpad = 1;
+ k++;
+ }
+ }
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+#if IS_REVERSE(REALTYPE)
+ assert(list_last(&head) != NULL);
+#endif
+ ts_hash("fill / add_tail", "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19");
+
+#if !IS_ATOMIC(REALTYPE)
+ struct list_head other;
+
+ list_init(&other);
+ list_swap_all(&head, &other);
+
+ assert(list_count(&head) == 0);
+ assert(!list_first(&head));
+ assert(list_count(&other) == k);
+ assert(list_first(&other) != NULL);
+#if IS_REVERSE(REALTYPE)
+ assert(!list_last(&head));
+ assert(list_last(&other) != NULL);
+#endif
+ ts_hash_head(
+ &other, "swap1",
+ "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19");
+
+ prng_swap = prng_new(0x1234dead);
+ l = 0;
+ for (i = 0; i < NITEM_SWAP; i++) {
+ j = prng_rand(prng_swap) % NITEM_SWAP;
+ if (itmswap[j].scratchpad == 0) {
+ list_add_tail(&head, &itmswap[j]);
+ itmswap[j].scratchpad = 1;
+ l++;
+ }
+ }
+ assert(list_count(&head) == l);
+ assert(list_first(&head) != NULL);
+ ts_hash_head(
+ &head, "swap-fill",
+ "833e6ae437e322dfbd36eda8cfc33a61109be735b43f15d256c05e52d1b01909");
+
+ list_swap_all(&head, &other);
+
+ assert(list_count(&other) == l);
+ assert(list_first(&other));
+ ts_hash_head(
+ &other, "swap2a",
+ "833e6ae437e322dfbd36eda8cfc33a61109be735b43f15d256c05e52d1b01909");
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+ ts_hash_head(
+ &head, "swap2b",
+ "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19");
+
+ while (list_pop(&other))
+ ;
+ list_fini(&other);
+ prng_free(prng_swap);
+
+ ts_ref("swap-cleanup");
+#endif
+
+ for (i = 0; i < NITEM / 2; i++) {
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 1) {
+ assert(list_del(&head, &itm[j]) != NULL);
+ itm[j].scratchpad = 0;
+ k--;
+ }
+ }
+ ts_hash("del-prng", "86d568a95eb429dab3162976c5a5f3f75aabc835932cd682aa280b6923549564");
+
+#if !IS_ATOMIC(REALTYPE)
+ for (i = 0; i < NITEM; i++) {
+ assertf(list_member(&head, &itm[i]) == itm[i].scratchpad,
+ "%zu should:%d is:%d", i, itm[i].scratchpad,
+ list_member(&head, &itm[i]));
+ assertf(list_anywhere(&itm[i]) == itm[i].scratchpad,
+ "%zu should:%d is:%d", i, itm[i].scratchpad,
+ list_anywhere(&itm[i]));
+ }
+ ts_hash("member", "86d568a95eb429dab3162976c5a5f3f75aabc835932cd682aa280b6923549564");
+#endif
+
+ l = 0;
+ while (l < (k / 4) && (prev = list_pop(&head))) {
+ assert(prev->scratchpad != 0);
+
+ prev->scratchpad = 0;
+ l++;
+ }
+ ts_hash("pop#1", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d");
+
+#if !IS_ATOMIC(REALTYPE)
+ for (i = 0; i < NITEM; i++) {
+ assertf(list_member(&head, &itm[i]) == itm[i].scratchpad,
+ "%zu should:%d is:%d", i, itm[i].scratchpad,
+ list_member(&head, &itm[i]));
+ assertf(list_anywhere(&itm[i]) == itm[i].scratchpad,
+ "%zu should:%d is:%d", i, itm[i].scratchpad,
+ list_anywhere(&itm[i]));
+ }
+ ts_hash("member", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d");
+#endif
+#if IS_REVERSE(REALTYPE)
+ i = 0;
+ prev = NULL;
+
+ frr_rev_each (list, &head, item) {
+ assert(item->scratchpad != 0);
+ assert(list_next(&head, item) == prev);
+
+ i++;
+ prev = item;
+ }
+ assert(list_first(&head) == prev);
+ assert(list_count(&head) == i);
+ ts_hash("reverse-walk", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d");
+#endif
+
+ while ((item = list_pop(&head))) {
+ assert(item->scratchpad != 0);
+
+ item->scratchpad = 0;
+ l++;
+ }
+ assert(l == k);
+ assert(list_count(&head) == 0);
+ assert(list_first(&head) == NULL);
+ ts_hash("pop#2", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
+
+ prng_free(prng);
+ prng = prng_new(0x1e5a2d69);
+
+ k = 0;
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 0) {
+ list_add_head(&head, &itm[j]);
+ itm[j].scratchpad = 1;
+ k++;
+ }
+ }
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+ ts_hash("fill / add_head", "3084d8f8a28b8c756ccc0a92d60d86f6d776273734ddc3f9e1d89526f5ca2795");
+
+ for (i = 0; i < NITEM / 2; i++) {
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 1) {
+ assert(list_del(&head, &itm[j]) != NULL);
+ itm[j].scratchpad = 0;
+ k--;
+ }
+ }
+ ts_hash("del-prng", "dc916fa7ea4418792c7c8232d74df2887f9975ead4222f4b977be6bc0b52285e");
+
+ l = 0;
+ while ((item = list_pop(&head))) {
+ assert(item->scratchpad != 0);
+
+ item->scratchpad = 0;
+ l++;
+ }
+ assert(l == k);
+ assert(list_count(&head) == 0);
+ assert(list_first(&head) == NULL);
+ ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
+
+ prng_free(prng);
+ prng = prng_new(0x692d1e5a);
+
+ k = 0;
+ for (i = 0; i < NITEM; i++) {
+ j = prng_rand(prng) % NITEM;
+ if (itm[j].scratchpad == 0) {
+ if (prng_rand(prng) & 1) {
+ list_add_tail(&head, &itm[j]);
+ } else {
+ list_add_head(&head, &itm[j]);
+ }
+ itm[j].scratchpad = 1;
+ k++;
+ }
+ }
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+ ts_hash("fill / add_{head,tail}", "93fa180a575c96e4b6c3775c2de7843ee3254dd6ed5af699bbe155f994114b06");
+
+ for (i = 0; i < NITEM * 3; i++) {
+ int op = prng_rand(prng);
+ j = prng_rand(prng) % NITEM;
+
+ if (op & 1) {
+ /* delete or pop */
+ if (op & 2) {
+ item = list_pop(&head);
+ if (!item)
+ continue;
+ } else {
+ item = &itm[j];
+ if (item->scratchpad == 0)
+ continue;
+ assert(list_del(&head, item) != NULL);
+ }
+ item->scratchpad = 0;
+ k--;
+ } else {
+ item = &itm[j];
+ if (item->scratchpad != 0)
+ continue;
+
+ item->scratchpad = 1;
+ k++;
+
+ switch ((op >> 1) & 1) {
+ case 0:
+ list_add_head(&head, item);
+ break;
+ case 1:
+ list_add_tail(&head, item);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ }
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+ ts_hash("prng add/del", "4909f31d06bb006efca4dfeebddb8de071733ddf502f89b6d532155208bbc6df");
+
+#if !IS_ATOMIC(REALTYPE)
+ /* variant with add_after */
+
+ for (i = 0; i < NITEM * 3; i++) {
+ int op = prng_rand(prng);
+ j = prng_rand(prng) % NITEM;
+
+ if (op & 1) {
+ /* delete or pop */
+ if (op & 2) {
+ item = list_pop(&head);
+ if (!item)
+ continue;
+ } else {
+ item = &itm[j];
+ if (item->scratchpad == 0)
+ continue;
+ assert(list_del(&head, item) != NULL);
+ }
+ item->scratchpad = 0;
+ k--;
+ } else {
+ item = &itm[j];
+ if (item->scratchpad != 0)
+ continue;
+
+ item->scratchpad = 1;
+ k++;
+
+ switch ((op >> 1) & 3) {
+ case 0:
+ list_add_head(&head, item);
+ break;
+ case 1:
+ list_add_tail(&head, item);
+ break;
+ case 2:
+ case 3:
+ prev = NULL;
+ l = 0;
+ do {
+ j = prng_rand(prng) % NITEM;
+ prev = &itm[j];
+ if (prev->scratchpad == 0
+ || prev == item)
+ prev = NULL;
+ l++;
+ } while (!prev && l < 10);
+ list_add_after(&head, prev, item);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ }
+ assert(list_count(&head) == k);
+ assert(list_first(&head) != NULL);
+ ts_hash("prng add/after/del", "84c5fc83294eabebb9808ccbba32a303c4fca084db87ed1277d2bae1f8c5bee4");
+#endif
+
+ l = 0;
+#endif
+
+ while ((item = list_pop(&head))) {
+ assert(item->scratchpad != 0);
+
+ item->scratchpad = 0;
+ l++;
+ }
+ assert(l == k);
+ assert(list_count(&head) == 0);
+ assert(list_first(&head) == NULL);
+ ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
+
+ list_fini(&head);
+ ts_ref("fini");
+ ts_end();
+ prng_free(prng);
+ printfrr("%s end\n", str(TYPE));
+}
+
+#undef ts_hash
+#undef ts_hashx
+#undef ts_hash_head
+#undef ts_hash_headx
+
+#undef item
+#undef itm
+#undef itmswap
+#undef head
+#undef list
+#undef list_head
+#undef list_item
+#undef list_cmp
+#undef list_hash
+#undef list_init
+#undef list_fini
+#undef list_first
+#undef list_next
+#undef list_next_safe
+#undef list_const_first
+#undef list_const_next
+#undef list_last
+#undef list_prev
+#undef list_prev_safe
+#undef list_const_last
+#undef list_const_prev
+#undef list_count
+#undef list_add
+#undef list_add_head
+#undef list_add_tail
+#undef list_add_after
+#undef list_find
+#undef list_find_lt
+#undef list_find_gteq
+#undef list_member
+#undef list_anywhere
+#undef list_del
+#undef list_pop
+#undef list_swap_all
+
+#undef REALTYPE
+#undef TYPE
diff --git a/tests/lib/test_typelist.py b/tests/lib/test_typelist.py
new file mode 100644
index 0000000..fe3499c
--- /dev/null
+++ b/tests/lib/test_typelist.py
@@ -0,0 +1,21 @@
+import frrtest
+
+
+class TestTypelist(frrtest.TestMultiOut):
+ program = "./test_typelist"
+
+
+TestTypelist.onesimple("LIST end")
+TestTypelist.onesimple("DLIST end")
+TestTypelist.onesimple("ATOMLIST end")
+TestTypelist.onesimple("HEAP end")
+TestTypelist.onesimple("SORTLIST_UNIQ end")
+TestTypelist.onesimple("SORTLIST_NONUNIQ end")
+TestTypelist.onesimple("HASH end")
+TestTypelist.onesimple("HASH_collisions end")
+TestTypelist.onesimple("SKIPLIST_UNIQ end")
+TestTypelist.onesimple("SKIPLIST_NONUNIQ end")
+TestTypelist.onesimple("RBTREE_UNIQ end")
+TestTypelist.onesimple("RBTREE_NONUNIQ end")
+TestTypelist.onesimple("ATOMSORT_UNIQ end")
+TestTypelist.onesimple("ATOMSORT_NONUNIQ end")
diff --git a/tests/lib/test_versioncmp.c b/tests/lib/test_versioncmp.c
new file mode 100644
index 0000000..84ae06e
--- /dev/null
+++ b/tests/lib/test_versioncmp.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * frr_version_cmp() tests
+ * Copyright (C) 2018 David Lamparter for NetDEF, Inc.
+ */
+#include <zebra.h>
+#include <defaults.h>
+
+static const char *rel(int x)
+{
+ if (x < 0)
+ return "<";
+ if (x > 0)
+ return ">";
+ return "==";
+}
+
+static int fail;
+
+static void compare(const char *a, const char *b, int expect)
+{
+ int result = frr_version_cmp(a, b);
+
+ if (expect == result)
+ printf("\"%s\" %s \"%s\"\n", a, rel(result), b);
+ else {
+ printf("\"%s\" %s \"%s\", expected %s!\n", a, rel(result), b,
+ rel(expect));
+ fail = 1;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ compare("", "", 0);
+ compare("1", "1", 0);
+ compare("1.0", "1.00", 0);
+ compare("10.0", "1", 1);
+ compare("10.0", "2", 1);
+ compare("2.1", "10.0", -1);
+ compare("1.1.1", "1.1.0", 1);
+ compare("1.0a", "1.0", 1);
+ compare("1.0a", "1.0b", -1);
+ compare("1.0a10", "1.0a2", 1);
+ compare("1.00a2", "1.0a2", 0);
+ compare("1.00a2", "1.0a3", -1);
+ compare("1.0-dev", "1.0", 1);
+ compare("1.0~foo", "1.0", -1);
+ compare("1.0~1", "1.0~0", 1);
+ compare("1.00~1", "1.0~0", 1);
+ printf("final tally: %s\n", fail ? "FAILED" : "ok");
+ return fail;
+}
diff --git a/tests/lib/test_versioncmp.py b/tests/lib/test_versioncmp.py
new file mode 100644
index 0000000..8ded53b
--- /dev/null
+++ b/tests/lib/test_versioncmp.py
@@ -0,0 +1,8 @@
+import frrtest
+
+
+class TestVersionCmp(frrtest.TestMultiOut):
+ program = "./test_versioncmp"
+
+
+TestVersionCmp.exit_cleanly()
diff --git a/tests/lib/test_xref.c b/tests/lib/test_xref.c
new file mode 100644
index 0000000..fca0d47
--- /dev/null
+++ b/tests/lib/test_xref.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * xref tests
+ * Copyright (C) 2020 David Lamparter for NetDEF, Inc.
+ */
+
+#include <zebra.h>
+#include "xref.h"
+#include "log.h"
+
+/*
+ * "lib/test_xref.c" (only 1 directory component included)
+ * "logging call"
+ * 0x00000003 (network byte order - LOG_ERR)
+ * 0x00000000 (network byte order - EC / zero here)
+ *
+ * note there are no '\0' terminators included for the strings
+ *
+ * SHA256
+ * => 71a65ce6e81517f642c8f55fb2af6f181f7df54357913b5b577aa61a663fdd4c
+ * & 0f -> 0x01 'H'
+ * & f001 -> 0x07 '7'
+ * & 3e -> 0x13 'K'
+ * & c007 -> 0x12 'J'
+ * & f8 -> 0x0b 'B'
+ * etc.
+ * (for reference: base32ch[] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ")
+ *
+ * (bits are consumed starting with the lowest bit, and the first character
+ * only consumes 4 bits and has the 5th bit at 1)
+ */
+
+static const char *expect_uid = "H7KJB-67TBH";
+static bool test_logcall(void)
+{
+ zlog_err("logging call");
+
+ return true;
+}
+
+static void check_xref(const struct xref *xref, bool *found, bool *error)
+{
+ const char *file = xref->file, *p;
+
+ p = strrchr(file, '/');
+ if (p)
+ file = p + 1;
+
+ if (strcmp(file, "test_xref.c"))
+ return;
+ if (xref->type != XREFT_LOGMSG)
+ return;
+ if (strcmp(xref->func, "test_logcall"))
+ return;
+
+ printf("xref: %s:%d %s() type=%d uid=%s\n",
+ xref->file, xref->line, xref->func, xref->type,
+ xref->xrefdata ? xref->xrefdata->uid : "--");
+
+ if (*found) {
+ printf("duplicate xref!\n");
+ *error = true;
+ }
+
+ const struct xref_logmsg *logmsg;
+
+ logmsg = container_of(xref, struct xref_logmsg, xref);
+ if (strcmp(logmsg->fmtstring, "logging call")) {
+ printf("log message mismatch!\n");
+ *error = true;
+ }
+ if (logmsg->priority != LOG_ERR || logmsg->ec != 0) {
+ printf("metadata mismatch!\n");
+ *error = true;
+ }
+
+ *found = true;
+
+ if (!xref->xrefdata) {
+ printf("no unique ID?\n");
+ *error = true;
+ return;
+ }
+
+ if (strcmp(xref->xrefdata->uid, expect_uid)) {
+ printf("unique ID mismatch, expected %s, got %s\n",
+ expect_uid, xref->xrefdata->uid);
+ *error = true;
+ }
+}
+
+static bool test_lookup(void)
+{
+ struct xref_block *xb;
+ bool found = false, error = false;
+
+ for (xb = xref_blocks; xb; xb = xb->next) {
+ const struct xref * const *xrefp;
+
+ for (xrefp = xb->start; xrefp < xb->stop; xrefp++) {
+ const struct xref *xref = *xrefp;
+
+ if (!xref)
+ continue;
+
+ check_xref(xref, &found, &error);
+ }
+ }
+ return found && !error;
+}
+
+bool (*tests[])(void) = {
+ test_lookup,
+ test_logcall,
+};
+
+XREF_SETUP();
+
+int main(int argc, char **argv)
+{
+ zlog_aux_init("NONE: ", ZLOG_DISABLED);
+
+ for (unsigned int i = 0; i < array_size(tests); i++)
+ if (!tests[i]())
+ return 1;
+ return 0;
+}
diff --git a/tests/lib/test_xref.py b/tests/lib/test_xref.py
new file mode 100644
index 0000000..8c3db3e
--- /dev/null
+++ b/tests/lib/test_xref.py
@@ -0,0 +1,6 @@
+import frrtest
+
+class TestXref(frrtest.TestMultiOut):
+ program = './test_xref'
+
+TestXref.exit_cleanly()
diff --git a/tests/lib/test_zlog.c b/tests/lib/test_zlog.c
new file mode 100644
index 0000000..95d9056
--- /dev/null
+++ b/tests/lib/test_zlog.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Zlog tests.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Quentin Young
+ */
+#include <zebra.h>
+#include <memory.h>
+#include "log.h"
+#include "network.h"
+
+/* maximum amount of data to hexdump */
+#define MAXDATA 16384
+
+/*
+ * Test hexdump functionality.
+ *
+ * At the moment, not crashing is considered success.
+ */
+static bool test_zlog_hexdump(void)
+{
+ unsigned int nl = 1;
+
+ do {
+ uint8_t d[nl];
+
+ for (unsigned int i = 0; i < nl; i++)
+ d[i] = frr_weak_random();
+ zlog_hexdump(d, nl - 1);
+
+ nl += 1 + (nl / 2);
+ } while (nl <= MAXDATA);
+
+ return true;
+}
+
+bool (*tests[])(void) = {
+ test_zlog_hexdump,
+};
+
+int main(int argc, char **argv)
+{
+ zlog_aux_init("NONE: ", ZLOG_DISABLED);
+
+ for (unsigned int i = 0; i < array_size(tests); i++)
+ if (!tests[i]())
+ return 1;
+ return 0;
+}
diff --git a/tests/lib/test_zlog.py b/tests/lib/test_zlog.py
new file mode 100644
index 0000000..2a2d54e
--- /dev/null
+++ b/tests/lib/test_zlog.py
@@ -0,0 +1,5 @@
+import frrtest
+
+
+class TestZlog(frrtest.TestMultiOut):
+ program = "./test_zlog"
diff --git a/tests/lib/test_zmq.c b/tests/lib/test_zmq.c
new file mode 100644
index 0000000..2cd9d47
--- /dev/null
+++ b/tests/lib/test_zmq.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ZeroMQ event test
+ * Copyright (C) 2017 David Lamparter, for NetDEF, Inc.
+ */
+
+#include <zebra.h>
+#include "memory.h"
+#include "sigevent.h"
+#include "frr_zmq.h"
+
+DEFINE_MTYPE_STATIC(LIB, TESTBUF, "zmq test buffer");
+DEFINE_MTYPE_STATIC(LIB, ZMQMSG, "zmq message");
+
+static struct event_loop *master;
+
+static void msg_buf_free(void *data, void *hint)
+{
+ XFREE(MTYPE_TESTBUF, data);
+}
+
+static int recv_delim(void *zmqsock)
+{
+ /* receive delim */
+ zmq_msg_t zdelim;
+ int more;
+ zmq_msg_init(&zdelim);
+ zmq_msg_recv(&zdelim, zmqsock, 0);
+ more = zmq_msg_more(&zdelim);
+ zmq_msg_close(&zdelim);
+ return more;
+}
+static void send_delim(void *zmqsock)
+{
+ /* Send delim */
+ zmq_msg_t zdelim;
+ zmq_msg_init(&zdelim);
+ zmq_msg_send(&zdelim, zmqsock, ZMQ_SNDMORE);
+ zmq_msg_close(&zdelim);
+}
+static void run_client(int syncfd)
+{
+ int i, j;
+ char buf[32];
+ char dummy;
+ void *zmqctx = NULL;
+ void *zmqsock;
+ int more;
+
+ read(syncfd, &dummy, 1);
+
+ zmqctx = zmq_ctx_new();
+ zmq_ctx_set(zmqctx, ZMQ_IPV6, 1);
+
+ zmqsock = zmq_socket(zmqctx, ZMQ_DEALER);
+ if (zmq_connect(zmqsock, "tcp://127.0.0.1:17171")) {
+ perror("zmq_connect");
+ exit(1);
+ }
+
+ /* single-part */
+ for (i = 0; i < 8; i++) {
+ snprintf(buf, sizeof(buf), "msg #%d %c%c%c", i, 'a' + i,
+ 'b' + i, 'c' + i);
+ printf("client send: %s\n", buf);
+ fflush(stdout);
+ send_delim(zmqsock);
+ zmq_send(zmqsock, buf, strlen(buf) + 1, 0);
+ more = recv_delim(zmqsock);
+ while (more) {
+ zmq_recv(zmqsock, buf, sizeof(buf), 0);
+ printf("client recv: %s\n", buf);
+ size_t len = sizeof(more);
+ if (zmq_getsockopt(zmqsock, ZMQ_RCVMORE, &more, &len))
+ break;
+ }
+ }
+
+ /* multipart */
+ for (i = 2; i < 5; i++) {
+ printf("---\n");
+ send_delim(zmqsock);
+ zmq_msg_t part;
+ for (j = 1; j <= i; j++) {
+ char *dyn = XMALLOC(MTYPE_TESTBUF, 32);
+
+ snprintf(dyn, 32, "part %d/%d", j, i);
+ printf("client send: %s\n", dyn);
+ fflush(stdout);
+
+ zmq_msg_init_data(&part, dyn, strlen(dyn) + 1,
+ msg_buf_free, NULL);
+ zmq_msg_send(&part, zmqsock, j < i ? ZMQ_SNDMORE : 0);
+ }
+
+ recv_delim(zmqsock);
+ do {
+ char *data;
+
+ zmq_msg_recv(&part, zmqsock, 0);
+ data = zmq_msg_data(&part);
+ more = zmq_msg_more(&part);
+ printf("client recv (more: %d): %s\n", more, data);
+ } while (more);
+ zmq_msg_close(&part);
+ }
+
+ /* write callback */
+ printf("---\n");
+ snprintf(buf, sizeof(buf), "Done receiving");
+ printf("client send: %s\n", buf);
+ fflush(stdout);
+ send_delim(zmqsock);
+ zmq_send(zmqsock, buf, strlen(buf) + 1, 0);
+ /* wait for message from server */
+ more = recv_delim(zmqsock);
+ while (more) {
+ zmq_recv(zmqsock, buf, sizeof(buf), 0);
+ printf("client recv: %s\n", buf);
+ size_t len = sizeof(more);
+ if (zmq_getsockopt(zmqsock, ZMQ_RCVMORE, &more, &len))
+ break;
+ }
+
+ zmq_close(zmqsock);
+ zmq_ctx_term(zmqctx);
+}
+
+static struct frrzmq_cb *cb;
+
+static void recv_id_and_delim(void *zmqsock, zmq_msg_t *msg_id)
+{
+ /* receive id */
+ zmq_msg_init(msg_id);
+ zmq_msg_recv(msg_id, zmqsock, 0);
+ /* receive delim */
+ recv_delim(zmqsock);
+}
+static void send_id_and_delim(void *zmqsock, zmq_msg_t *msg_id)
+{
+ /* Send Id */
+ zmq_msg_send(msg_id, zmqsock, ZMQ_SNDMORE);
+ send_delim(zmqsock);
+}
+static void serverwritefn(void *arg, void *zmqsock)
+{
+ zmq_msg_t *msg_id = (zmq_msg_t *)arg;
+ char buf[32] = "Test write callback";
+ size_t i;
+
+ for (i = 0; i < strlen(buf); i++)
+ buf[i] = toupper(buf[i]);
+ printf("server send: %s\n", buf);
+ fflush(stdout);
+ send_id_and_delim(zmqsock, msg_id);
+ zmq_send(zmqsock, buf, strlen(buf) + 1, 0);
+
+ /* send just once */
+ frrzmq_thread_cancel(&cb, &cb->write);
+
+ zmq_msg_close(msg_id);
+ XFREE(MTYPE_ZMQMSG, msg_id);
+}
+static void serverpartfn(void *arg, void *zmqsock, zmq_msg_t *msg,
+ unsigned partnum)
+{
+ static int num = 0;
+ int more = zmq_msg_more(msg);
+ char *in = zmq_msg_data(msg);
+ size_t i;
+ zmq_msg_t reply;
+ char *out;
+
+ /* Id */
+ if (partnum == 0) {
+ send_id_and_delim(zmqsock, msg);
+ return;
+ }
+ /* Delim */
+ if (partnum == 1)
+ return;
+
+
+ printf("server recv part %u (more: %d): %s\n", partnum, more, in);
+ fflush(stdout);
+
+ out = XMALLOC(MTYPE_TESTBUF, strlen(in) + 1);
+ for (i = 0; i < strlen(in); i++)
+ out[i] = toupper(in[i]);
+ out[i] = '\0';
+ zmq_msg_init_data(&reply, out, strlen(out) + 1, msg_buf_free, NULL);
+ zmq_msg_send(&reply, zmqsock, ZMQ_SNDMORE);
+
+ if (more)
+ return;
+
+ out = XMALLOC(MTYPE_TESTBUF, 32);
+ snprintf(out, 32, "msg# was %u", partnum);
+ zmq_msg_init_data(&reply, out, strlen(out) + 1, msg_buf_free, NULL);
+ zmq_msg_send(&reply, zmqsock, 0);
+
+ zmq_msg_close(&reply);
+
+ if (++num < 7)
+ return;
+
+ /* write callback test */
+ char buf[32];
+ zmq_msg_t *msg_id = XMALLOC(MTYPE_ZMQMSG, sizeof(zmq_msg_t));
+ recv_id_and_delim(zmqsock, msg_id);
+ zmq_recv(zmqsock, buf, sizeof(buf), 0);
+ printf("server recv: %s\n", buf);
+ fflush(stdout);
+
+ frrzmq_event_add_write_msg(master, serverwritefn, NULL, msg_id, zmqsock,
+ &cb);
+}
+
+static void serverfn(void *arg, void *zmqsock)
+{
+ static int num = 0;
+
+ zmq_msg_t msg_id;
+ char buf[32];
+ size_t i;
+
+ recv_id_and_delim(zmqsock, &msg_id);
+ zmq_recv(zmqsock, buf, sizeof(buf), 0);
+
+ printf("server recv: %s\n", buf);
+ fflush(stdout);
+ for (i = 0; i < strlen(buf); i++)
+ buf[i] = toupper(buf[i]);
+ send_id_and_delim(zmqsock, &msg_id);
+ zmq_msg_close(&msg_id);
+ zmq_send(zmqsock, buf, strlen(buf) + 1, 0);
+
+ if (++num < 4)
+ return;
+
+ /* change to multipart callback */
+ frrzmq_thread_cancel(&cb, &cb->read);
+ frrzmq_thread_cancel(&cb, &cb->write);
+
+ frrzmq_event_add_read_part(master, serverpartfn, NULL, NULL, zmqsock,
+ &cb);
+}
+
+static void sigchld(void)
+{
+ printf("child exited.\n");
+ frrzmq_thread_cancel(&cb, &cb->read);
+ frrzmq_thread_cancel(&cb, &cb->write);
+}
+
+static struct frr_signal_t sigs[] = {
+ {
+ .signal = SIGCHLD,
+ .handler = sigchld,
+ },
+};
+
+static void run_server(int syncfd)
+{
+ void *zmqsock;
+ char dummy = 0;
+ struct event t;
+
+ master = event_master_create(NULL);
+ signal_init(master, array_size(sigs), sigs);
+ frrzmq_init();
+
+ zmqsock = zmq_socket(frrzmq_context, ZMQ_ROUTER);
+ if (zmq_bind(zmqsock, "tcp://*:17171")) {
+ perror("zmq_bind");
+ exit(1);
+ }
+
+ frrzmq_event_add_read_msg(master, serverfn, NULL, NULL, zmqsock, &cb);
+
+ write(syncfd, &dummy, sizeof(dummy));
+ while (event_fetch(master, &t))
+ event_call(&t);
+
+ zmq_close(zmqsock);
+ frrzmq_finish();
+ event_master_free(master);
+ log_memstats_stderr("test");
+}
+
+int main(void)
+{
+ int syncpipe[2];
+ pid_t child;
+
+ if (pipe(syncpipe)) {
+ perror("pipe");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ exit(1);
+ } else if (child == 0) {
+ run_client(syncpipe[0]);
+ exit(0);
+ }
+
+ run_server(syncpipe[1]);
+ exit(0);
+}
diff --git a/tests/lib/test_zmq.py b/tests/lib/test_zmq.py
new file mode 100644
index 0000000..5f6189d
--- /dev/null
+++ b/tests/lib/test_zmq.py
@@ -0,0 +1,14 @@
+import frrtest
+import pytest
+import os
+
+
+class TestZMQ(frrtest.TestRefOut):
+ program = "./test_zmq"
+
+ @pytest.mark.skipif(
+ 'S["ZEROMQ_TRUE"]=""\n' not in open("../config.status").readlines(),
+ reason="ZEROMQ not enabled",
+ )
+ def test_refout(self):
+ return super(TestZMQ, self).test_refout()
diff --git a/tests/lib/test_zmq.refout b/tests/lib/test_zmq.refout
new file mode 100644
index 0000000..acac505
--- /dev/null
+++ b/tests/lib/test_zmq.refout
@@ -0,0 +1,67 @@
+client send: msg #0 abc
+server recv: msg #0 abc
+client recv: MSG #0 ABC
+client send: msg #1 bcd
+server recv: msg #1 bcd
+client recv: MSG #1 BCD
+client send: msg #2 cde
+server recv: msg #2 cde
+client recv: MSG #2 CDE
+client send: msg #3 def
+server recv: msg #3 def
+client recv: MSG #3 DEF
+client send: msg #4 efg
+server recv part 2 (more: 0): msg #4 efg
+client recv: MSG #4 EFG
+client recv: msg# was 2
+client send: msg #5 fgh
+server recv part 2 (more: 0): msg #5 fgh
+client recv: MSG #5 FGH
+client recv: msg# was 2
+client send: msg #6 ghi
+server recv part 2 (more: 0): msg #6 ghi
+client recv: MSG #6 GHI
+client recv: msg# was 2
+client send: msg #7 hij
+server recv part 2 (more: 0): msg #7 hij
+client recv: MSG #7 HIJ
+client recv: msg# was 2
+---
+client send: part 1/2
+client send: part 2/2
+server recv part 2 (more: 1): part 1/2
+server recv part 3 (more: 0): part 2/2
+client recv (more: 1): PART 1/2
+client recv (more: 1): PART 2/2
+client recv (more: 0): msg# was 3
+---
+client send: part 1/3
+client send: part 2/3
+client send: part 3/3
+server recv part 2 (more: 1): part 1/3
+server recv part 3 (more: 1): part 2/3
+server recv part 4 (more: 0): part 3/3
+client recv (more: 1): PART 1/3
+client recv (more: 1): PART 2/3
+client recv (more: 1): PART 3/3
+client recv (more: 0): msg# was 4
+---
+client send: part 1/4
+client send: part 2/4
+client send: part 3/4
+client send: part 4/4
+server recv part 2 (more: 1): part 1/4
+server recv part 3 (more: 1): part 2/4
+server recv part 4 (more: 1): part 3/4
+server recv part 5 (more: 0): part 4/4
+client recv (more: 1): PART 1/4
+client recv (more: 1): PART 2/4
+client recv (more: 1): PART 3/4
+client recv (more: 1): PART 4/4
+client recv (more: 0): msg# was 5
+---
+client send: Done receiving
+server recv: Done receiving
+server send: TEST WRITE CALLBACK
+client recv: TEST WRITE CALLBACK
+child exited.
diff --git a/tests/ospf6d/subdir.am b/tests/ospf6d/subdir.am
new file mode 100644
index 0000000..ef1f40c
--- /dev/null
+++ b/tests/ospf6d/subdir.am
@@ -0,0 +1,19 @@
+if !OSPF6D
+PYTEST_IGNORE += --ignore=ospf6d/
+endif
+OSPF6_TEST_LDADD = ospf6d/libospf6.a $(ALL_TESTS_LDADD)
+
+
+if OSPF6D
+check_PROGRAMS += tests/ospf6d/test_lsdb
+endif
+tests_ospf6d_test_lsdb_CFLAGS = $(TESTS_CFLAGS)
+tests_ospf6d_test_lsdb_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_ospf6d_test_lsdb_LDADD = $(OSPF6_TEST_LDADD)
+tests_ospf6d_test_lsdb_SOURCES = tests/ospf6d/test_lsdb.c tests/lib/cli/common_cli.c
+clippy_scan += tests/ospf6d/test_lsdb.c
+EXTRA_DIST += \
+ tests/ospf6d/test_lsdb.py \
+ tests/ospf6d/test_lsdb.in \
+ tests/ospf6d/test_lsdb.refout \
+ # end
diff --git a/tests/ospf6d/test_lsdb.c b/tests/ospf6d/test_lsdb.c
new file mode 100644
index 0000000..f9df735
--- /dev/null
+++ b/tests/ospf6d/test_lsdb.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * CLI/command dummy handling tester
+ *
+ * Copyright (C) 2015 by David Lamparter,
+ * for Open Source Routing / NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "vector.h"
+#include "vty.h"
+
+#include "ospf6d/ospf6_lsa.h"
+#include "ospf6d/ospf6_lsdb.h"
+
+#include "tests/lib/cli/common_cli.h"
+#include "tests/ospf6d/test_lsdb_clippy.c"
+
+static struct ospf6_lsdb *lsdb;
+
+static struct ospf6_lsa **lsas = NULL;
+static size_t lsa_count = 0;
+
+static void lsa_check_resize(size_t len)
+{
+ struct ospf6_lsa **templsas;
+
+ if (lsa_count >= len)
+ return;
+ templsas = realloc(lsas, len * sizeof(lsas[0]));
+ if (templsas)
+ lsas = templsas;
+ else
+ return;
+ memset(lsas + lsa_count, 0, sizeof(lsas[0]) * (len - lsa_count));
+
+ lsa_count = len;
+}
+
+DEFPY(lsa_set, lsa_set_cmd,
+ "lsa set (0-999999)$idx {type (0-65535)|id A.B.C.D|adv A.B.C.D}",
+ "LSA\n"
+ "set\n"
+ "LSA index in array\n"
+ "OSPF6 type code\n"
+ "OSPF6 type code\n"
+ "LS-ID\n"
+ "LS-ID\n"
+ "Advertising router\n"
+ "Advertising router\n")
+{
+ struct ospf6_lsa_header hdr;
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.type = htons(type);
+ hdr.id = id.s_addr;
+ hdr.adv_router = adv.s_addr;
+
+ lsa_check_resize(idx + 1);
+ if (lsas[idx])
+ ospf6_lsa_unlock(&lsas[idx]);
+ lsas[idx] = ospf6_lsa_create_headeronly(&hdr);
+ ospf6_lsa_lock(lsas[idx]);
+ return CMD_SUCCESS;
+}
+
+DEFPY(lsa_drop, lsa_drop_cmd,
+ "lsa drop (0-999999)$idx",
+ "LSA\n"
+ "drop reference\n"
+ "LSA index in array\n")
+{
+ if ((size_t)idx >= lsa_count)
+ return CMD_SUCCESS;
+ if (lsas[idx]->lock != 1)
+ vty_out(vty, "refcount at %u\n", lsas[idx]->lock);
+ ospf6_lsa_unlock(&lsas[idx]);
+ lsas[idx] = NULL;
+ return CMD_SUCCESS;
+}
+
+
+DEFPY(lsdb_add, lsdb_add_cmd,
+ "lsdb add (0-999999)$idx",
+ "LSDB\n"
+ "insert LSA into LSDB\n"
+ "LSA index in array\n")
+{
+ ospf6_lsdb_add(lsas[idx], lsdb);
+ return CMD_SUCCESS;
+}
+
+DEFPY(lsdb_remove, lsdb_remove_cmd,
+ "lsdb remove (0-999999)$idx",
+ "LSDB\n"
+ "remove LSA from LSDB\n"
+ "LSA index in array\n")
+{
+ ospf6_lsdb_remove(lsas[idx], lsdb);
+ return CMD_SUCCESS;
+}
+
+static void lsa_show_oneline(struct vty *vty, struct ospf6_lsa *lsa)
+{
+ char adv_router[64], id[64];
+
+ if (!lsa) {
+ vty_out(vty, "lsa = NULL\n");
+ return;
+ }
+ inet_ntop(AF_INET, &lsa->header->id, id, sizeof(id));
+ inet_ntop(AF_INET, &lsa->header->adv_router, adv_router,
+ sizeof(adv_router));
+ vty_out(vty, "type %u adv %s id %s\n", ntohs(lsa->header->type),
+ adv_router, id);
+}
+
+DEFPY(lsdb_walk, lsdb_walk_cmd,
+ "lsdb walk",
+ "LSDB\n"
+ "walk entries\n")
+{
+ struct ospf6_lsa *lsa, *lsanext;
+
+ unsigned cnt = 0;
+ for (ALL_LSDB(lsdb, lsa, lsanext)) {
+ lsa_show_oneline(vty, lsa);
+ cnt++;
+ }
+ vty_out(vty, "%u entries.\n", cnt);
+ return CMD_SUCCESS;
+}
+
+DEFPY(lsdb_walk_type, lsdb_walk_type_cmd,
+ "lsdb walk type (0-65535)",
+ "LSDB\n"
+ "walk entries\n"
+ "entry type\n"
+ "entry type\n")
+{
+ struct ospf6_lsa *lsa;
+ unsigned cnt = 0;
+ type = htons(type);
+ for (ALL_LSDB_TYPED(lsdb, type, lsa)) {
+ lsa_show_oneline(vty, lsa);
+ cnt++;
+ }
+ vty_out(vty, "%u entries.\n", cnt);
+ return CMD_SUCCESS;
+}
+
+DEFPY(lsdb_walk_type_adv, lsdb_walk_type_adv_cmd,
+ "lsdb walk type (0-65535) adv A.B.C.D",
+ "LSDB\n"
+ "walk entries\n"
+ "entry type\n"
+ "entry type\n"
+ "advertising router ID\n"
+ "advertising router ID\n")
+{
+ struct ospf6_lsa *lsa;
+ unsigned cnt = 0;
+ type = htons(type);
+ for (ALL_LSDB_TYPED_ADVRTR(lsdb, type, adv.s_addr, lsa)) {
+ lsa_show_oneline(vty, lsa);
+ cnt++;
+ }
+ vty_out(vty, "%u entries.\n", cnt);
+ return CMD_SUCCESS;
+}
+
+DEFPY(lsdb_get, lsdb_get_cmd,
+ "lsdb <get-next|get> type (0-65535) adv A.B.C.D id A.B.C.D",
+ "LSDB\n"
+ "get entry's successor\n"
+ "entry type\n"
+ "entry type\n"
+ "advertising router ID\n"
+ "advertising router ID\n"
+ "LS-ID\n"
+ "LS-ID\n")
+{
+ struct ospf6_lsa *lsa;
+ type = htons(type);
+ if (!strcmp(argv[1]->text, "get-next"))
+ lsa = ospf6_lsdb_lookup_next(type, id.s_addr, adv.s_addr, lsdb);
+ else
+ lsa = ospf6_lsdb_lookup(type, id.s_addr, adv.s_addr, lsdb);
+ lsa_show_oneline(vty, lsa);
+ return CMD_SUCCESS;
+}
+
+DEFPY(lsa_refcounts, lsa_refcounts_cmd,
+ "lsa refcounts",
+ "LSA\n"
+ "show reference counts\n")
+{
+ for (size_t i = 0; i < lsa_count; i++)
+ if (lsas[i])
+ vty_out(vty, "[%zu] %u\n", i, lsas[i]->lock);
+ return CMD_SUCCESS;
+}
+
+DEFPY(lsdb_create, lsdb_create_cmd,
+ "lsdb create",
+ "LSDB\n"
+ "create LSDB\n")
+{
+ if (lsdb)
+ ospf6_lsdb_delete(lsdb);
+ lsdb = ospf6_lsdb_create(NULL);
+ return CMD_SUCCESS;
+}
+
+DEFPY(lsdb_delete, lsdb_delete_cmd,
+ "lsdb delete",
+ "LSDB\n"
+ "delete LSDB\n")
+{
+ ospf6_lsdb_delete(lsdb);
+ lsdb = NULL;
+ return CMD_SUCCESS;
+}
+
+
+struct zebra_privs_t ospf6d_privs;
+
+void test_init(int argc, char **argv)
+{
+ ospf6_lsa_init();
+
+ install_element(ENABLE_NODE, &lsa_set_cmd);
+ install_element(ENABLE_NODE, &lsa_refcounts_cmd);
+ install_element(ENABLE_NODE, &lsa_drop_cmd);
+
+ install_element(ENABLE_NODE, &lsdb_create_cmd);
+ install_element(ENABLE_NODE, &lsdb_delete_cmd);
+
+ install_element(ENABLE_NODE, &lsdb_add_cmd);
+ install_element(ENABLE_NODE, &lsdb_remove_cmd);
+ install_element(ENABLE_NODE, &lsdb_walk_cmd);
+ install_element(ENABLE_NODE, &lsdb_walk_type_cmd);
+ install_element(ENABLE_NODE, &lsdb_walk_type_adv_cmd);
+ install_element(ENABLE_NODE, &lsdb_get_cmd);
+}
diff --git a/tests/ospf6d/test_lsdb.in b/tests/ospf6d/test_lsdb.in
new file mode 100644
index 0000000..0475f3d
--- /dev/null
+++ b/tests/ospf6d/test_lsdb.in
@@ -0,0 +1,72 @@
+lsa set 0 type 1 adv 1.2.3.4 id 0.0.0.1
+lsa set 1 type 1 adv 1.2.3.4 id 0.0.0.2
+lsa set 2 type 2 adv 1.2.3.4 id 0.0.0.3
+lsa set 3 type 2 adv 128.2.3.4 id 0.0.0.4
+lsa set 4 type 2 adv 128.2.3.4 id 0.0.0.5
+lsa set 5 type 3 adv 0.0.0.1 id 0.0.0.6
+lsa refcounts
+
+lsdb create
+
+lsdb walk
+lsdb walk type 1
+lsdb walk type 2
+lsdb get type 1 adv 1.2.3.4 id 0.0.0.2
+lsdb get-next type 1 adv 1.2.3.4 id 0.0.0.2
+lsa refcounts
+
+lsdb add 0
+lsdb add 1
+lsa refcounts
+
+lsdb walk
+lsdb walk type 1
+lsdb walk type 2
+lsdb get type 1 adv 1.2.3.4 id 0.0.0.2
+lsdb get-next type 1 adv 1.2.3.4 id 0.0.0.2
+lsa refcounts
+
+lsdb remove 0
+lsdb add 2
+lsdb add 3
+lsdb add 4
+lsa refcounts
+
+lsdb walk
+lsdb walk type 1
+lsdb walk type 2
+lsdb get type 1 adv 1.2.3.4 id 0.0.0.2
+lsdb get-next type 1 adv 1.2.3.4 id 0.0.0.2
+lsa refcounts
+
+lsdb add 5
+lsa refcounts
+
+lsdb walk
+lsdb walk type 1
+lsdb walk type 2
+lsdb get type 1 adv 1.2.3.4 id 0.0.0.2
+lsdb get-next type 1 adv 1.2.3.4 id 0.0.0.2
+lsa refcounts
+
+lsdb remove 1
+lsdb remove 5
+lsa refcounts
+
+lsdb walk
+lsdb walk type 1
+lsdb walk type 2
+lsdb get type 1 adv 1.2.3.4 id 0.0.0.2
+lsdb get-next type 1 adv 1.2.3.4 id 0.0.0.2
+lsa refcounts
+
+lsdb delete
+
+lsa refcounts
+lsa drop 0
+lsa drop 1
+lsa drop 2
+lsa drop 3
+lsa drop 4
+lsa drop 5
+
diff --git a/tests/ospf6d/test_lsdb.py b/tests/ospf6d/test_lsdb.py
new file mode 100644
index 0000000..6ada617
--- /dev/null
+++ b/tests/ospf6d/test_lsdb.py
@@ -0,0 +1,5 @@
+import frrtest
+
+
+class TestLSDB(frrtest.TestRefOut):
+ program = "./test_lsdb"
diff --git a/tests/ospf6d/test_lsdb.refout b/tests/ospf6d/test_lsdb.refout
new file mode 100644
index 0000000..8b17652
--- /dev/null
+++ b/tests/ospf6d/test_lsdb.refout
@@ -0,0 +1,192 @@
+test# lsa set 0 type 1 adv 1.2.3.4 id 0.0.0.1
+test# lsa set 1 type 1 adv 1.2.3.4 id 0.0.0.2
+test# lsa set 2 type 2 adv 1.2.3.4 id 0.0.0.3
+test# lsa set 3 type 2 adv 128.2.3.4 id 0.0.0.4
+test# lsa set 4 type 2 adv 128.2.3.4 id 0.0.0.5
+test# lsa set 5 type 3 adv 0.0.0.1 id 0.0.0.6
+test# lsa refcounts
+[0] 1
+[1] 1
+[2] 1
+[3] 1
+[4] 1
+[5] 1
+test#
+test# lsdb create
+test#
+test# lsdb walk
+0 entries.
+test# lsdb walk type 1
+0 entries.
+test# lsdb walk type 2
+0 entries.
+test# lsdb get type 1 adv 1.2.3.4 id 0.0.0.2
+lsa = NULL
+test# lsdb get-next type 1 adv 1.2.3.4 id 0.0.0.2
+lsa = NULL
+test# lsa refcounts
+[0] 1
+[1] 1
+[2] 1
+[3] 1
+[4] 1
+[5] 1
+test#
+test# lsdb add 0
+test# lsdb add 1
+test# lsa refcounts
+[0] 2
+[1] 2
+[2] 1
+[3] 1
+[4] 1
+[5] 1
+test#
+test# lsdb walk
+type 1 adv 1.2.3.4 id 0.0.0.1
+type 1 adv 1.2.3.4 id 0.0.0.2
+2 entries.
+test# lsdb walk type 1
+type 1 adv 1.2.3.4 id 0.0.0.1
+type 1 adv 1.2.3.4 id 0.0.0.2
+2 entries.
+test# lsdb walk type 2
+0 entries.
+test# lsdb get type 1 adv 1.2.3.4 id 0.0.0.2
+type 1 adv 1.2.3.4 id 0.0.0.2
+test# lsdb get-next type 1 adv 1.2.3.4 id 0.0.0.2
+lsa = NULL
+test# lsa refcounts
+[0] 2
+[1] 2
+[2] 1
+[3] 1
+[4] 1
+[5] 1
+test#
+test# lsdb remove 0
+test# lsdb add 2
+test# lsdb add 3
+test# lsdb add 4
+test# lsa refcounts
+[0] 1
+[1] 2
+[2] 2
+[3] 2
+[4] 2
+[5] 1
+test#
+test# lsdb walk
+type 1 adv 1.2.3.4 id 0.0.0.2
+type 2 adv 1.2.3.4 id 0.0.0.3
+type 2 adv 128.2.3.4 id 0.0.0.4
+type 2 adv 128.2.3.4 id 0.0.0.5
+4 entries.
+test# lsdb walk type 1
+type 1 adv 1.2.3.4 id 0.0.0.2
+1 entries.
+test# lsdb walk type 2
+type 2 adv 1.2.3.4 id 0.0.0.3
+type 2 adv 128.2.3.4 id 0.0.0.4
+type 2 adv 128.2.3.4 id 0.0.0.5
+3 entries.
+test# lsdb get type 1 adv 1.2.3.4 id 0.0.0.2
+type 1 adv 1.2.3.4 id 0.0.0.2
+test# lsdb get-next type 1 adv 1.2.3.4 id 0.0.0.2
+type 2 adv 1.2.3.4 id 0.0.0.3
+test# lsa refcounts
+[0] 1
+[1] 2
+[2] 2
+[3] 2
+[4] 2
+[5] 1
+test#
+test# lsdb add 5
+test# lsa refcounts
+[0] 1
+[1] 2
+[2] 2
+[3] 2
+[4] 2
+[5] 2
+test#
+test# lsdb walk
+type 1 adv 1.2.3.4 id 0.0.0.2
+type 2 adv 1.2.3.4 id 0.0.0.3
+type 2 adv 128.2.3.4 id 0.0.0.4
+type 2 adv 128.2.3.4 id 0.0.0.5
+type 3 adv 0.0.0.1 id 0.0.0.6
+5 entries.
+test# lsdb walk type 1
+type 1 adv 1.2.3.4 id 0.0.0.2
+1 entries.
+test# lsdb walk type 2
+type 2 adv 1.2.3.4 id 0.0.0.3
+type 2 adv 128.2.3.4 id 0.0.0.4
+type 2 adv 128.2.3.4 id 0.0.0.5
+3 entries.
+test# lsdb get type 1 adv 1.2.3.4 id 0.0.0.2
+type 1 adv 1.2.3.4 id 0.0.0.2
+test# lsdb get-next type 1 adv 1.2.3.4 id 0.0.0.2
+type 2 adv 1.2.3.4 id 0.0.0.3
+test# lsa refcounts
+[0] 1
+[1] 2
+[2] 2
+[3] 2
+[4] 2
+[5] 2
+test#
+test# lsdb remove 1
+test# lsdb remove 5
+test# lsa refcounts
+[0] 1
+[1] 1
+[2] 2
+[3] 2
+[4] 2
+[5] 1
+test#
+test# lsdb walk
+type 2 adv 1.2.3.4 id 0.0.0.3
+type 2 adv 128.2.3.4 id 0.0.0.4
+type 2 adv 128.2.3.4 id 0.0.0.5
+3 entries.
+test# lsdb walk type 1
+0 entries.
+test# lsdb walk type 2
+type 2 adv 1.2.3.4 id 0.0.0.3
+type 2 adv 128.2.3.4 id 0.0.0.4
+type 2 adv 128.2.3.4 id 0.0.0.5
+3 entries.
+test# lsdb get type 1 adv 1.2.3.4 id 0.0.0.2
+lsa = NULL
+test# lsdb get-next type 1 adv 1.2.3.4 id 0.0.0.2
+type 2 adv 1.2.3.4 id 0.0.0.3
+test# lsa refcounts
+[0] 1
+[1] 1
+[2] 2
+[3] 2
+[4] 2
+[5] 1
+test#
+test# lsdb delete
+test#
+test# lsa refcounts
+[0] 1
+[1] 1
+[2] 1
+[3] 1
+[4] 1
+[5] 1
+test# lsa drop 0
+test# lsa drop 1
+test# lsa drop 2
+test# lsa drop 3
+test# lsa drop 4
+test# lsa drop 5
+test#
+test#
+end.
diff --git a/tests/ospfd/.gitignore b/tests/ospfd/.gitignore
new file mode 100644
index 0000000..c659b64
--- /dev/null
+++ b/tests/ospfd/.gitignore
@@ -0,0 +1,3 @@
+/*_afl/*
+test_ospf_spf
+core
diff --git a/tests/ospfd/common.c b/tests/ospfd/common.c
new file mode 100644
index 0000000..e394186
--- /dev/null
+++ b/tests/ospfd/common.c
@@ -0,0 +1,249 @@
+#include <zebra.h>
+
+#include "lib/stream.h"
+#include "lib/vty.h"
+#include "lib/mpls.h"
+#include "lib/if.h"
+#include "lib/table.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_sr.h"
+
+#include "common.h"
+
+struct event_loop *master;
+struct zebra_privs_t ospfd_privs;
+
+
+struct ospf_topology *test_find_topology(const char *name)
+{
+ if (strmatch(name, "topo1"))
+ return &topo1;
+ else if (strmatch(name, "topo2"))
+ return &topo2;
+ else if (strmatch(name, "topo3"))
+ return &topo3;
+ else if (strmatch(name, "topo4"))
+ return &topo4;
+ else if (strmatch(name, "topo5"))
+ return &topo5;
+
+ return NULL;
+}
+
+int sort_paths(const void **path1, const void **path2)
+{
+ const struct ospf_path *p1 = *path1;
+ const struct ospf_path *p2 = *path2;
+
+ return (p1->nexthop.s_addr - p2->nexthop.s_addr);
+}
+
+void print_route_table(struct vty *vty, struct route_table *rt)
+{
+ struct route_node *rn;
+ struct ospf_route * or ;
+ struct listnode *pnode;
+ struct ospf_path *path;
+ struct mpls_label_stack *label_stack;
+ char buf[MPLS_LABEL_STRLEN];
+
+ for (rn = route_top(rt); rn; rn = route_next(rn)) {
+ if ((or = rn->info) == NULL)
+ continue;
+
+ vty_out(vty, "N %-18pFX %-15pI4 %d\n", &rn->p,
+ & or->u.std.area_id, or->cost);
+
+ list_sort(or->paths, sort_paths);
+
+ for (ALL_LIST_ELEMENTS_RO(or->paths, pnode, path)) {
+ if (path->nexthop.s_addr == 0)
+ continue;
+
+ vty_out(vty, " -> %pI4 with adv router %pI4",
+ &path->nexthop, &path->adv_router);
+
+ if (path->srni.backup_label_stack) {
+ label_stack = path->srni.backup_label_stack;
+ mpls_label2str(label_stack->num_labels,
+ label_stack->label, buf,
+ MPLS_LABEL_STRLEN,
+ ZEBRA_LSP_NONE, true);
+ vty_out(vty, " and backup path %s", buf);
+ }
+ vty_out(vty, "\n");
+ }
+ }
+}
+
+struct ospf_test_node *test_find_node(struct ospf_topology *topology,
+ const char *hostname)
+{
+ for (int i = 0; topology->nodes[i].hostname[0]; i++)
+ if (strmatch(hostname, topology->nodes[i].hostname))
+ return &topology->nodes[i];
+
+ return NULL;
+}
+
+static void inject_router_lsa(struct vty *vty, struct ospf *ospf,
+ struct ospf_topology *topology,
+ struct ospf_test_node *root,
+ struct ospf_test_node *tnode)
+{
+ struct ospf_area *area;
+ struct in_addr router_id;
+ struct in_addr adj_router_id;
+ struct prefix_ipv4 prefix;
+ struct in_addr data;
+ struct stream *s;
+ struct lsa_header *lsah;
+ struct ospf_lsa *new;
+ int length;
+ unsigned long putp;
+ uint16_t link_count;
+ struct ospf_test_node *tfound_adj_node;
+ struct ospf_test_adj *tadj;
+ bool is_self_lsa = false;
+
+ area = ospf->backbone;
+ inet_aton(tnode->router_id, &router_id);
+
+ if (strncmp(root->router_id, tnode->router_id, 256) == 0)
+ is_self_lsa = true;
+
+ s = stream_new(OSPF_MAX_LSA_SIZE);
+ lsa_header_set(s, LSA_OPTIONS_GET(area) | LSA_OPTIONS_NSSA_GET(area),
+ OSPF_ROUTER_LSA, router_id, router_id);
+
+ stream_putc(s, router_lsa_flags(area));
+ stream_putc(s, 0);
+
+ putp = stream_get_endp(s);
+ stream_putw(s, 0);
+
+ for (link_count = 0; tnode->adjacencies[link_count].hostname[0];
+ link_count++) {
+ tadj = &tnode->adjacencies[link_count];
+ tfound_adj_node = test_find_node(topology, tadj->hostname);
+ str2prefix_ipv4(tnode->adjacencies[link_count].network,
+ &prefix);
+
+ inet_aton(tfound_adj_node->router_id, &adj_router_id);
+ data.s_addr = prefix.prefix.s_addr;
+ link_info_set(&s, adj_router_id, data,
+ LSA_LINK_TYPE_POINTOPOINT, 0, tadj->metric);
+
+ masklen2ip(prefix.prefixlen, &data);
+ link_info_set(&s, prefix.prefix, data, LSA_LINK_TYPE_STUB, 0,
+ tadj->metric);
+ }
+
+ /* Don't forget the node itself (just a stub) */
+ str2prefix_ipv4(tnode->router_id, &prefix);
+ data.s_addr = 0xffffffff;
+ link_info_set(&s, prefix.prefix, data, LSA_LINK_TYPE_STUB, 0, 0);
+
+ /* Take twice the link count (for P2P and stub) plus the local stub */
+ stream_putw_at(s, putp, (2 * link_count) + 1);
+
+ length = stream_get_endp(s);
+ lsah = (struct lsa_header *)STREAM_DATA(s);
+ lsah->length = htons(length);
+
+ new = ospf_lsa_new_and_data(length);
+ new->area = area;
+ new->vrf_id = area->ospf->vrf_id;
+
+ if (is_self_lsa)
+ SET_FLAG(new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED);
+
+ memcpy(new->data, lsah, length);
+ stream_free(s);
+
+ ospf_lsdb_add(area->lsdb, new);
+
+ if (is_self_lsa) {
+ ospf_lsa_unlock(&area->router_lsa_self);
+ area->router_lsa_self = ospf_lsa_lock(new);
+ }
+}
+
+static void inject_sr_db_entry(struct vty *vty, struct ospf_test_node *tnode,
+ struct ospf_topology *topology)
+{
+ struct ospf_test_node *tfound_adj_node;
+ struct ospf_test_adj *tadj;
+ struct in_addr router_id;
+ struct in_addr remote_id;
+ struct sr_node *srn;
+ struct sr_prefix *srp;
+ struct sr_link *srl;
+ int link_count;
+
+ inet_aton(tnode->router_id, &router_id);
+
+ srn = ospf_sr_node_create(&router_id);
+
+ srn->srgb.range_size = 8000;
+ srn->srgb.lower_bound = 16000;
+ srn->msd = 16;
+
+ srn->srlb.range_size = 1000;
+ srn->srlb.lower_bound = 15000;
+
+ /* Prefix SID */
+ srp = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix));
+ srp->adv_router = router_id;
+ srp->sid = tnode->label;
+ srp->srn = srn;
+
+ listnode_add(srn->ext_prefix, srp);
+
+ /* Adjacency SIDs for all adjacencies */
+ for (link_count = 0; tnode->adjacencies[link_count].hostname[0];
+ link_count++) {
+ tadj = &tnode->adjacencies[link_count];
+ tfound_adj_node = test_find_node(topology, tadj->hostname);
+
+ srl = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_link));
+ srl->adv_router = router_id;
+
+ inet_aton(tfound_adj_node->router_id, &remote_id);
+ srl->remote_id = remote_id;
+
+ srl->type = ADJ_SID;
+ srl->sid[0] = srn->srlb.lower_bound + tadj->label;
+ srl->srn = srn;
+
+ listnode_add(srn->ext_link, srl);
+ }
+}
+
+int topology_load(struct vty *vty, struct ospf_topology *topology,
+ struct ospf_test_node *root, struct ospf *ospf)
+{
+ struct ospf_test_node *tnode;
+
+ for (int i = 0; topology->nodes[i].hostname[0]; i++) {
+ tnode = &topology->nodes[i];
+
+ /* Inject a router LSA for each node, used for SPF */
+ inject_router_lsa(vty, ospf, topology, root, tnode);
+
+ /*
+ * SR information could also be inected via LSAs, but directly
+ * filling the SR DB with labels is just easier.
+ */
+ inject_sr_db_entry(vty, tnode, topology);
+ }
+
+ return 0;
+}
diff --git a/tests/ospfd/common.h b/tests/ospfd/common.h
new file mode 100644
index 0000000..18e412b
--- /dev/null
+++ b/tests/ospfd/common.h
@@ -0,0 +1,47 @@
+#ifndef _COMMON_OSPF_H
+#define _COMMON_OSPF_H
+
+#define MAX_ADJACENCIES 8
+#define MAX_NODES 12
+
+struct ospf_test_adj {
+ char hostname[256];
+ char network[256];
+ uint32_t metric;
+ mpls_label_t label;
+};
+
+struct ospf_test_node {
+ char hostname[256];
+ const char *router_id;
+ mpls_label_t label;
+ struct ospf_test_adj adjacencies[MAX_ADJACENCIES + 1];
+};
+
+struct ospf_topology {
+ struct ospf_test_node nodes[MAX_NODES + 1];
+};
+
+/* Prototypes. */
+extern struct ospf_topology *test_find_topology(const char *name);
+extern struct ospf_test_node *test_find_node(struct ospf_topology *topology,
+ const char *hostname);
+extern int topology_load(struct vty *vty, struct ospf_topology *topology,
+ struct ospf_test_node *root, struct ospf *ospf);
+
+/* Global variables. */
+extern struct event_loop *master;
+extern struct ospf_topology topo1;
+extern struct ospf_topology topo2;
+extern struct ospf_topology topo3;
+extern struct ospf_topology topo4;
+extern struct ospf_topology topo5;
+extern struct zebra_privs_t ospfd_privs;
+
+/* For stable order in unit tests */
+extern int sort_paths(const void **path1, const void **path2);
+
+/* Print the routing table */
+extern void print_route_table(struct vty *vty, struct route_table *rt);
+
+#endif /* _COMMON_OSPF_H */
diff --git a/tests/ospfd/subdir.am b/tests/ospfd/subdir.am
new file mode 100644
index 0000000..5ed5b9d
--- /dev/null
+++ b/tests/ospfd/subdir.am
@@ -0,0 +1,21 @@
+if !OSPFD
+PYTEST_IGNORE += --ignore=ospfd/
+endif
+OSPFD_TEST_LDADD = ospfd/libfrrospf.a $(ALL_TESTS_LDADD)
+noinst_HEADERS += \
+ tests/ospfd/common.h \
+ # end
+
+
+if OSPFD
+check_PROGRAMS += tests/ospfd/test_ospf_spf
+endif
+tests_ospfd_test_ospf_spf_CFLAGS = $(TESTS_CFLAGS)
+tests_ospfd_test_ospf_spf_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_ospfd_test_ospf_spf_LDADD = $(OSPFD_TEST_LDADD)
+tests_ospfd_test_ospf_spf_SOURCES = tests/ospfd/test_ospf_spf.c tests/ospfd/common.c tests/ospfd/topologies.c
+EXTRA_DIST += \
+ tests/ospfd/test_ospf_spf.py \
+ tests/ospfd/test_ospf_spf.in \
+ tests/ospfd/test_ospf_spf.refout \
+ # end
diff --git a/tests/ospfd/test_ospf_spf.c b/tests/ospfd/test_ospf_spf.c
new file mode 100644
index 0000000..fc6b8e8
--- /dev/null
+++ b/tests/ospfd/test_ospf_spf.c
@@ -0,0 +1,305 @@
+#include <zebra.h>
+
+#include "getopt.h"
+#include "frrevent.h"
+#include <lib/version.h>
+#include "vty.h"
+#include "command.h"
+#include "log.h"
+#include "vrf.h"
+#include "table.h"
+#include "mpls.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_ti_lfa.h"
+#include "ospfd/ospf_vty.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_sr.h"
+
+#include "common.h"
+
+DECLARE_RBTREE_UNIQ(p_spaces, struct p_space, p_spaces_item,
+ p_spaces_compare_func);
+DECLARE_RBTREE_UNIQ(q_spaces, struct q_space, q_spaces_item,
+ q_spaces_compare_func);
+
+static struct ospf *test_init(struct ospf_test_node *root)
+{
+ struct ospf *ospf;
+ struct ospf_area *area;
+ struct in_addr area_id;
+ struct in_addr router_id;
+
+ ospf = ospf_new_alloc(0, VRF_DEFAULT_NAME);
+
+ area_id.s_addr = OSPF_AREA_BACKBONE;
+ area = ospf_area_new(ospf, area_id);
+ listnode_add_sort(ospf->areas, area);
+
+ inet_aton(root->router_id, &router_id);
+ ospf->router_id = router_id;
+ ospf->router_id_static = router_id;
+ ospf->ti_lfa_enabled = true;
+
+ return ospf;
+}
+
+static void test_run_spf(struct vty *vty, struct ospf *ospf,
+ enum protection_type protection_type, bool verbose)
+{
+ struct route_table *new_table, *new_rtrs;
+ struct route_table *all_rtrs = NULL;
+ struct ospf_area *area;
+ struct p_space *p_space;
+ struct q_space *q_space;
+ char label_buf[MPLS_LABEL_STRLEN];
+ char res_buf[PROTECTED_RESOURCE_STRLEN];
+
+ /* Just use the backbone for testing */
+ area = ospf->backbone;
+
+ new_table = route_table_init();
+ new_rtrs = route_table_init();
+ all_rtrs = route_table_init();
+
+ /* dryrun true, root_node false */
+ ospf_spf_calculate(area, area->router_lsa_self, new_table, all_rtrs,
+ new_rtrs, true, false);
+
+ if (verbose) {
+ vty_out(vty, "SPF Tree without TI-LFA backup paths:\n\n");
+ ospf_spf_print(vty, area->spf, 0);
+
+ vty_out(vty,
+ "\nRouting Table without TI-LFA backup paths:\n\n");
+ print_route_table(vty, new_table);
+ }
+
+ if (verbose)
+ vty_out(vty, "\n... generating TI-LFA backup paths ...\n");
+
+ /* TI-LFA testrun */
+ ospf_ti_lfa_generate_p_spaces(area, protection_type);
+ ospf_ti_lfa_insert_backup_paths(area, new_table);
+
+ /* Print P/Q space information */
+ if (verbose) {
+ vty_out(vty, "\nP and Q space info:\n");
+ frr_each (p_spaces, area->p_spaces, p_space) {
+ ospf_print_protected_resource(
+ p_space->protected_resource, res_buf);
+ vty_out(vty, "\nP Space for root %pI4 and %s\n",
+ &p_space->root->id, res_buf);
+ ospf_spf_print(vty, p_space->root, 0);
+
+ frr_each (q_spaces, p_space->q_spaces, q_space) {
+ vty_out(vty,
+ "\nQ Space for destination %pI4:\n",
+ &q_space->root->id);
+ ospf_spf_print(vty, q_space->root, 0);
+ if (q_space->label_stack) {
+ mpls_label2str(
+ q_space->label_stack
+ ->num_labels,
+ q_space->label_stack->label,
+ label_buf, MPLS_LABEL_STRLEN,
+ ZEBRA_LSP_NONE, true);
+ vty_out(vty, "\nLabel stack: %s\n",
+ label_buf);
+ } else {
+ vty_out(vty,
+ "\nLabel stack not generated!\n");
+ }
+ }
+
+ vty_out(vty, "\nPost-convergence SPF Tree:\n");
+ ospf_spf_print(vty, p_space->pc_spf, 0);
+ }
+ }
+
+ /* Cleanup */
+ ospf_ti_lfa_free_p_spaces(area);
+ ospf_spf_cleanup(area->spf, area->spf_vertex_list);
+
+ /*
+ * Print the new routing table which is augmented with TI-LFA backup
+ * paths (label stacks).
+ */
+ if (verbose)
+ vty_out(vty,
+ "\n\nFinal Routing Table including backup paths:\n\n");
+
+ print_route_table(vty, new_table);
+}
+
+static int test_run(struct vty *vty, struct ospf_topology *topology,
+ struct ospf_test_node *root,
+ enum protection_type protection_type, bool verbose)
+{
+ struct ospf *ospf;
+
+ ospf = test_init(root);
+
+ /* Inject LSAs into the OSPF backbone according to the topology */
+ if (topology_load(vty, topology, root, ospf)) {
+ vty_out(vty, "%% Failed to load topology\n");
+ return CMD_WARNING;
+ }
+
+ if (verbose) {
+ vty_out(vty, "\n");
+ show_ip_ospf_database_summary(vty, ospf, 0, NULL);
+ }
+
+ test_run_spf(vty, ospf, protection_type, verbose);
+
+ return 0;
+}
+
+DEFUN(test_ospf, test_ospf_cmd,
+ "test ospf topology WORD root HOSTNAME ti-lfa [node-protection] [verbose]",
+ "Test mode\n"
+ "Choose OSPF for SPF testing\n"
+ "Network topology to choose\n"
+ "Name of the network topology to choose\n"
+ "Root node to choose\n"
+ "Hostname of the root node to choose\n"
+ "Use Topology-Independent LFA\n"
+ "Use node protection (default is link protection)\n"
+ "Verbose output\n")
+{
+ struct ospf_topology *topology;
+ struct ospf_test_node *root;
+ enum protection_type protection_type = OSPF_TI_LFA_LINK_PROTECTION;
+ int idx = 0;
+ bool verbose = false;
+
+ /* Parse topology. */
+ argv_find(argv, argc, "topology", &idx);
+ topology = test_find_topology(argv[idx + 1]->arg);
+ if (!topology) {
+ vty_out(vty, "%% Topology not found\n");
+ return CMD_WARNING;
+ }
+
+ argv_find(argv, argc, "root", &idx);
+ root = test_find_node(topology, argv[idx + 1]->arg);
+ if (!root) {
+ vty_out(vty, "%% Root not found\n");
+ return CMD_WARNING;
+ }
+
+ if (argv_find(argv, argc, "node-protection", &idx))
+ protection_type = OSPF_TI_LFA_NODE_PROTECTION;
+
+ if (argv_find(argv, argc, "verbose", &idx))
+ verbose = true;
+
+ return test_run(vty, topology, root, protection_type, verbose);
+}
+
+static void vty_do_exit(int isexit)
+{
+ printf("\nend.\n");
+
+ cmd_terminate();
+ vty_terminate();
+ event_master_free(master);
+
+ if (!isexit)
+ exit(0);
+}
+
+struct option longopts[] = {{"help", no_argument, NULL, 'h'},
+ {"debug", no_argument, NULL, 'd'},
+ {0} };
+
+/* Help information display. */
+static void usage(char *progname, int status)
+{
+ if (status != 0)
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ progname);
+ else {
+ printf("Usage : %s [OPTION...]\n\
+ospfd SPF test program.\n\n\
+-u, --debug Enable debugging\n\
+-h, --help Display this help and exit\n\
+\n\
+Report bugs to %s\n",
+ progname, FRR_BUG_ADDRESS);
+ }
+ exit(status);
+}
+
+int main(int argc, char **argv)
+{
+ char *p;
+ char *progname;
+ struct event thread;
+ bool debug = false;
+
+ /* Set umask before anything for security */
+ umask(0027);
+
+ /* get program name */
+ progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
+
+ while (1) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "hd", longopts, 0);
+
+ if (opt == EOF)
+ break;
+
+ switch (opt) {
+ case 0:
+ break;
+ case 'd':
+ debug = true;
+ break;
+ case 'h':
+ usage(progname, 0);
+ break;
+ default:
+ usage(progname, 1);
+ break;
+ }
+ }
+
+ /* master init. */
+ master = event_master_create(NULL);
+
+ /* Library inits. */
+ cmd_init(1);
+ cmd_hostname_set("test");
+ vty_init(master, false);
+ if (debug)
+ zlog_aux_init("NONE: ", LOG_DEBUG);
+ else
+ zlog_aux_init("NONE: ", ZLOG_DISABLED);
+
+ /* Install test command. */
+ install_element(VIEW_NODE, &test_ospf_cmd);
+
+ /* needed for SR DB init */
+ ospf_vty_init();
+ ospf_sr_init();
+
+ term_debug_ospf_ti_lfa = 1;
+
+ /* Read input from .in file. */
+ vty_stdio(vty_do_exit);
+
+ /* Fetch next active thread. */
+ while (event_fetch(master, &thread))
+ event_call(&thread);
+
+ /* Not reached. */
+ exit(0);
+}
diff --git a/tests/ospfd/test_ospf_spf.in b/tests/ospfd/test_ospf_spf.in
new file mode 100644
index 0000000..f1e7467
--- /dev/null
+++ b/tests/ospfd/test_ospf_spf.in
@@ -0,0 +1,10 @@
+test ospf topology topo1 root rt1 ti-lfa
+test ospf topology topo1 root rt1 ti-lfa node-protection
+test ospf topology topo2 root rt1 ti-lfa
+test ospf topology topo2 root rt1 ti-lfa node-protection
+test ospf topology topo3 root rt1 ti-lfa
+test ospf topology topo3 root rt1 ti-lfa node-protection
+test ospf topology topo4 root rt1 ti-lfa
+test ospf topology topo4 root rt1 ti-lfa node-protection
+test ospf topology topo5 root rt1 ti-lfa
+test ospf topology topo5 root rt1 ti-lfa node-protection
diff --git a/tests/ospfd/test_ospf_spf.py b/tests/ospfd/test_ospf_spf.py
new file mode 100644
index 0000000..92a1c6a
--- /dev/null
+++ b/tests/ospfd/test_ospf_spf.py
@@ -0,0 +1,4 @@
+import frrtest
+
+class TestOspfSPF(frrtest.TestRefOut):
+ program = './test_ospf_spf'
diff --git a/tests/ospfd/test_ospf_spf.refout b/tests/ospfd/test_ospf_spf.refout
new file mode 100644
index 0000000..d1e3c7b
--- /dev/null
+++ b/tests/ospfd/test_ospf_spf.refout
@@ -0,0 +1,130 @@
+test# test ospf topology topo1 root rt1 ti-lfa
+N 1.1.1.1/32 0.0.0.0 0
+N 2.2.2.2/32 0.0.0.0 10
+ -> 10.0.1.2 with adv router 2.2.2.2 and backup path 15002
+N 3.3.3.3/32 0.0.0.0 10
+ -> 10.0.3.2 with adv router 3.3.3.3 and backup path 15001
+N 10.0.1.0/24 0.0.0.0 10
+N 10.0.2.0/24 0.0.0.0 20
+ -> 10.0.1.2 with adv router 2.2.2.2 and backup path 15002
+ -> 10.0.3.2 with adv router 3.3.3.3 and backup path 15001
+N 10.0.3.0/24 0.0.0.0 10
+test# test ospf topology topo1 root rt1 ti-lfa node-protection
+N 1.1.1.1/32 0.0.0.0 0
+N 2.2.2.2/32 0.0.0.0 10
+ -> 10.0.1.2 with adv router 2.2.2.2
+N 3.3.3.3/32 0.0.0.0 10
+ -> 10.0.3.2 with adv router 3.3.3.3
+N 10.0.1.0/24 0.0.0.0 10
+N 10.0.2.0/24 0.0.0.0 20
+ -> 10.0.1.2 with adv router 2.2.2.2
+ -> 10.0.3.2 with adv router 3.3.3.3
+N 10.0.3.0/24 0.0.0.0 10
+test# test ospf topology topo2 root rt1 ti-lfa
+N 1.1.1.1/32 0.0.0.0 0
+N 2.2.2.2/32 0.0.0.0 10
+ -> 10.0.1.2 with adv router 2.2.2.2 and backup path 15002
+N 3.3.3.3/32 0.0.0.0 20
+ -> 10.0.1.2 with adv router 3.3.3.3 and backup path 15002
+N 10.0.1.0/24 0.0.0.0 10
+N 10.0.2.0/24 0.0.0.0 20
+ -> 10.0.1.2 with adv router 2.2.2.2 and backup path 15002
+N 10.0.3.0/24 0.0.0.0 30
+test# test ospf topology topo2 root rt1 ti-lfa node-protection
+N 1.1.1.1/32 0.0.0.0 0
+N 2.2.2.2/32 0.0.0.0 10
+ -> 10.0.1.2 with adv router 2.2.2.2
+N 3.3.3.3/32 0.0.0.0 20
+ -> 10.0.1.2 with adv router 3.3.3.3 and backup path 15002
+N 10.0.1.0/24 0.0.0.0 10
+N 10.0.2.0/24 0.0.0.0 20
+ -> 10.0.1.2 with adv router 2.2.2.2
+N 10.0.3.0/24 0.0.0.0 30
+test# test ospf topology topo3 root rt1 ti-lfa
+N 1.1.1.1/32 0.0.0.0 0
+N 2.2.2.2/32 0.0.0.0 10
+ -> 10.0.1.2 with adv router 2.2.2.2 and backup path 16030
+N 3.3.3.3/32 0.0.0.0 20
+ -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001
+N 4.4.4.4/32 0.0.0.0 10
+ -> 10.0.4.2 with adv router 4.4.4.4 and backup path 15001/15004
+N 10.0.1.0/24 0.0.0.0 10
+N 10.0.2.0/24 0.0.0.0 30
+ -> 10.0.1.2 with adv router 2.2.2.2 and backup path 16030
+N 10.0.3.0/24 0.0.0.0 20
+ -> 10.0.4.2 with adv router 4.4.4.4 and backup path 15001/15004
+N 10.0.4.0/24 0.0.0.0 10
+test# test ospf topology topo3 root rt1 ti-lfa node-protection
+N 1.1.1.1/32 0.0.0.0 0
+N 2.2.2.2/32 0.0.0.0 10
+ -> 10.0.1.2 with adv router 2.2.2.2
+N 3.3.3.3/32 0.0.0.0 20
+ -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001
+N 4.4.4.4/32 0.0.0.0 10
+ -> 10.0.4.2 with adv router 4.4.4.4
+N 10.0.1.0/24 0.0.0.0 10
+N 10.0.2.0/24 0.0.0.0 30
+ -> 10.0.1.2 with adv router 2.2.2.2
+N 10.0.3.0/24 0.0.0.0 20
+ -> 10.0.4.2 with adv router 4.4.4.4
+N 10.0.4.0/24 0.0.0.0 10
+test# test ospf topology topo4 root rt1 ti-lfa
+N 1.1.1.1/32 0.0.0.0 0
+N 2.2.2.2/32 0.0.0.0 10
+ -> 10.0.1.2 with adv router 2.2.2.2 and backup path 16030/15006
+N 3.3.3.3/32 0.0.0.0 20
+ -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001/15004
+N 4.4.4.4/32 0.0.0.0 10
+ -> 10.0.4.2 with adv router 4.4.4.4 and backup path 15001/15004
+N 10.0.1.0/24 0.0.0.0 10
+N 10.0.2.0/24 0.0.0.0 60
+ -> 10.0.1.2 with adv router 2.2.2.2 and backup path 16030/15006
+N 10.0.3.0/24 0.0.0.0 20
+ -> 10.0.4.2 with adv router 4.4.4.4 and backup path 15001/15004
+N 10.0.4.0/24 0.0.0.0 10
+test# test ospf topology topo4 root rt1 ti-lfa node-protection
+N 1.1.1.1/32 0.0.0.0 0
+N 2.2.2.2/32 0.0.0.0 10
+ -> 10.0.1.2 with adv router 2.2.2.2
+N 3.3.3.3/32 0.0.0.0 20
+ -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001/15004
+N 4.4.4.4/32 0.0.0.0 10
+ -> 10.0.4.2 with adv router 4.4.4.4
+N 10.0.1.0/24 0.0.0.0 10
+N 10.0.2.0/24 0.0.0.0 60
+ -> 10.0.1.2 with adv router 2.2.2.2
+N 10.0.3.0/24 0.0.0.0 20
+ -> 10.0.4.2 with adv router 4.4.4.4
+N 10.0.4.0/24 0.0.0.0 10
+test# test ospf topology topo5 root rt1 ti-lfa
+N 1.1.1.1/32 0.0.0.0 0
+N 2.2.2.2/32 0.0.0.0 30
+ -> 10.0.4.2 with adv router 2.2.2.2 and backup path 15001
+N 3.3.3.3/32 0.0.0.0 20
+ -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001/15004
+N 4.4.4.4/32 0.0.0.0 10
+ -> 10.0.4.2 with adv router 4.4.4.4 and backup path 15001/15004/15006
+N 10.0.1.0/24 0.0.0.0 40
+ -> 10.0.4.2 with adv router 2.2.2.2 and backup path 15001
+N 10.0.2.0/24 0.0.0.0 30
+ -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001/15004
+N 10.0.3.0/24 0.0.0.0 20
+ -> 10.0.4.2 with adv router 4.4.4.4 and backup path 15001/15004/15006
+N 10.0.4.0/24 0.0.0.0 10
+test# test ospf topology topo5 root rt1 ti-lfa node-protection
+N 1.1.1.1/32 0.0.0.0 0
+N 2.2.2.2/32 0.0.0.0 30
+ -> 10.0.4.2 with adv router 2.2.2.2 and backup path 15001
+N 3.3.3.3/32 0.0.0.0 20
+ -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001/15004
+N 4.4.4.4/32 0.0.0.0 10
+ -> 10.0.4.2 with adv router 4.4.4.4
+N 10.0.1.0/24 0.0.0.0 40
+ -> 10.0.4.2 with adv router 2.2.2.2 and backup path 15001
+N 10.0.2.0/24 0.0.0.0 30
+ -> 10.0.4.2 with adv router 3.3.3.3 and backup path 15001/15004
+N 10.0.3.0/24 0.0.0.0 20
+ -> 10.0.4.2 with adv router 4.4.4.4
+N 10.0.4.0/24 0.0.0.0 10
+test#
+end.
diff --git a/tests/ospfd/topologies.c b/tests/ospfd/topologies.c
new file mode 100644
index 0000000..2dc611c
--- /dev/null
+++ b/tests/ospfd/topologies.c
@@ -0,0 +1,575 @@
+#include <zebra.h>
+
+#include "mpls.h"
+#include "if.h"
+
+#include "ospfd/ospfd.h"
+
+#include "common.h"
+
+/*
+ * +---------+ +---------+
+ * | | | |
+ * | RT1 |eth-rt2 eth-rt1| RT2 |
+ * | 1.1.1.1 +---------------------+ 2.2.2.2 |
+ * | | 10.0.1.0/24 | |
+ * +---------+ +---------+
+ * |eth-rt3 eth-rt3|
+ * | |
+ * |10.0.3.0/24 |
+ * | |
+ * |eth-rt1 |
+ * +---------+ |
+ * | |eth-rt2 10.0.2.0/24|
+ * | RT3 +--------------------------+
+ * | 3.3.3.3 |
+ * | |
+ * +---------+
+ *
+ * Link Protection:
+ * P and Q spaces overlap here, hence just one P/Q node regardless of which
+ * link is protected. Hence the backup label stack just has one label.
+ *
+ * Node Protection:
+ * Obviously no backup paths involved.
+ */
+struct ospf_topology topo1 = {
+ .nodes =
+ {
+ {
+ .hostname = "rt1",
+ .router_id = "1.1.1.1",
+ .label = 10,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt2",
+ .network =
+ "10.0.1.1/24",
+ .metric = 10,
+ .label = 1,
+ },
+ {
+ .hostname = "rt3",
+ .network =
+ "10.0.3.1/24",
+ .metric = 10,
+ .label = 2,
+ },
+ },
+ },
+ {
+ .hostname = "rt2",
+ .router_id = "2.2.2.2",
+ .label = 20,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt1",
+ .network =
+ "10.0.1.2/24",
+ .metric = 10,
+ .label = 3,
+ },
+ {
+ .hostname = "rt3",
+ .network =
+ "10.0.2.1/24",
+ .metric = 10,
+ .label = 4,
+ },
+ },
+ },
+ {
+ .hostname = "rt3",
+ .router_id = "3.3.3.3",
+ .label = 30,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt1",
+ .network =
+ "10.0.3.2/24",
+ .metric = 10,
+ .label = 5,
+ },
+ {
+ .hostname = "rt2",
+ .network =
+ "10.0.2.2/24",
+ .metric = 10,
+ .label = 6,
+ },
+ },
+ },
+ },
+};
+
+
+/*
+ * +---------+ +---------+
+ * | | | |
+ * | RT1 |eth-rt2 eth-rt1| RT2 |
+ * | 1.1.1.1 +---------------------+ 2.2.2.2 |
+ * | | 10.0.1.0/24 (10) | |
+ * +---------+ +---------+
+ * |eth-rt3 eth-rt3|
+ * | |
+ * |10.0.3.0/24 (30) |
+ * | |
+ * |eth-rt1 |
+ * +---------+ |
+ * | |eth-rt2 10.0.2.0/24|(10)
+ * | RT3 +--------------------------+
+ * | 3.3.3.3 |
+ * | |
+ * +---------+
+ *
+ * Link Protection:
+ * Regarding the subnet 10.0.1.0/24, the P space of RT1 is just RT1 itself
+ * while the Q space of RT3 consists of RT3 and RT2. Hence the P and Q
+ * nodes are disjunct (tricky: the root node is the P node here). For the
+ * backup label stack just one label is necessary.
+ *
+ * Node Protection:
+ * For protected node RT2 and route from RT1 to RT3 there is just the backup
+ * path consisting of the label 15002.
+ */
+struct ospf_topology topo2 = {
+ .nodes =
+ {
+ {
+ .hostname = "rt1",
+ .router_id = "1.1.1.1",
+ .label = 10,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt2",
+ .network =
+ "10.0.1.1/24",
+ .metric = 10,
+ .label = 1,
+ },
+ {
+ .hostname = "rt3",
+ .network =
+ "10.0.3.1/24",
+ .metric = 30,
+ .label = 2,
+ },
+ },
+ },
+ {
+ .hostname = "rt2",
+ .router_id = "2.2.2.2",
+ .label = 20,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt1",
+ .network =
+ "10.0.1.2/24",
+ .metric = 10,
+ .label = 3,
+ },
+ {
+ .hostname = "rt3",
+ .network =
+ "10.0.2.1/24",
+ .metric = 10,
+ .label = 4,
+ },
+ },
+ },
+ {
+ .hostname = "rt3",
+ .router_id = "3.3.3.3",
+ .label = 30,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt1",
+ .network =
+ "10.0.3.2/24",
+ .metric = 30,
+ .label = 5,
+ },
+ {
+ .hostname = "rt2",
+ .network =
+ "10.0.2.2/24",
+ .metric = 10,
+ .label = 6,
+ },
+ },
+ },
+ },
+};
+
+/*
+ * +---------+ +---------+
+ * | | | |
+ * | RT1 |eth-rt4 eth-rt1| RT4 |
+ * | 1.1.1.1 +---------------------+ 4.4.4.4 |
+ * | | 10.0.4.0/24 (10) | |
+ * +---------+ +---------+
+ * |eth-rt2 eth-rt3|
+ * | |
+ * |10.0.1.0/24 (10) |
+ * | 10.0.3.0/24 (10) |
+ * |eth-rt1 eth-rt4|
+ * +---------+ +---------+
+ * | |eth-rt3 eth-rt2| |
+ * | RT2 +---------------------+ RT3 |
+ * | 2.2.2.2 | 10.0.2.0/24 (20) | 3.3.3.3 |
+ * | | | |
+ * +---------+ +---------+
+ *
+ * Link Protection:
+ * Regarding the protected subnet 10.0.4.0/24, the P and Q spaces for root RT1
+ * and destination RT4 are disjunct and the P node is RT2 while RT3 is the Q
+ * node. Hence the backup label stack here is 16020/15004. Note that here the
+ * P and Q nodes are neither the root nor the destination nodes, so this is a
+ * case where you really need a label stack consisting of two labels.
+ *
+ * Node Protection:
+ * For the protected node RT4 and the route from RT1 to RT3 there is a backup
+ * path with the single label 15001.
+ */
+struct ospf_topology topo3 = {
+ .nodes =
+ {
+ {
+ .hostname = "rt1",
+ .router_id = "1.1.1.1",
+ .label = 10,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt2",
+ .network =
+ "10.0.1.1/24",
+ .metric = 10,
+ .label = 1,
+ },
+ {
+ .hostname = "rt4",
+ .network =
+ "10.0.4.1/24",
+ .metric = 10,
+ .label = 2,
+ },
+ },
+ },
+ {
+ .hostname = "rt2",
+ .router_id = "2.2.2.2",
+ .label = 20,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt1",
+ .network =
+ "10.0.1.2/24",
+ .metric = 10,
+ .label = 3,
+ },
+ {
+ .hostname = "rt3",
+ .network =
+ "10.0.2.1/24",
+ .metric = 20,
+ .label = 4,
+ },
+ },
+ },
+ {
+ .hostname = "rt3",
+ .router_id = "3.3.3.3",
+ .label = 30,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt2",
+ .network =
+ "10.0.2.2/24",
+ .metric = 20,
+ .label = 5,
+ },
+ {
+ .hostname = "rt4",
+ .network =
+ "10.0.3.1/24",
+ .metric = 10,
+ .label = 6,
+ },
+ },
+ },
+ {
+ .hostname = "rt4",
+ .router_id = "4.4.4.4",
+ .label = 40,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt1",
+ .network =
+ "10.0.4.2/24",
+ .metric = 10,
+ .label = 7,
+ },
+ {
+ .hostname = "rt3",
+ .network =
+ "10.0.3.2/24",
+ .metric = 10,
+ .label = 8,
+ },
+ },
+ },
+ },
+};
+
+/*
+ * +---------+ +---------+
+ * | | | |
+ * | RT1 |eth-rt4 eth-rt1| RT4 |
+ * | 1.1.1.1 +---------------------+ 4.4.4.4 |
+ * | | 10.0.4.0/24 (10) | |
+ * +---------+ +---------+
+ * |eth+rt2 eth-rt3|
+ * | |
+ * |10.0.1.0/24 (10) |
+ * | 10.0.3.0/24 (10) |
+ * |eth-rt1 eth-rt4|
+ * +---------+ +---------+
+ * | |eth-rt3 eth-rt2| |
+ * | RT2 +---------------------+ RT3 |
+ * | 2.2.2.2 | 10.0.2.0/24 (40) | 3.3.3.3 |
+ * | | | |
+ * +---------+ +---------+
+ *
+ * This case was specifically created for Node Protection with RT4 as
+ * protected node from the perspective of RT1. Note the weight of 40
+ * on the link between RT2 and RT3.
+ * The P space of RT1 is just RT2 while the Q space of RT3 is empty.
+ * This means that the P and Q spaces are disjunct and there are two
+ * labels needed to get from RT1 to RT3.
+ */
+struct ospf_topology topo4 = {
+ .nodes =
+ {
+ {
+ .hostname = "rt1",
+ .router_id = "1.1.1.1",
+ .label = 10,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt2",
+ .network =
+ "10.0.1.1/24",
+ .metric = 10,
+ .label = 1,
+ },
+ {
+ .hostname = "rt4",
+ .network =
+ "10.0.4.1/24",
+ .metric = 10,
+ .label = 2,
+ },
+ },
+ },
+ {
+ .hostname = "rt2",
+ .router_id = "2.2.2.2",
+ .label = 20,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt1",
+ .network =
+ "10.0.1.2/24",
+ .metric = 10,
+ .label = 3,
+ },
+ {
+ .hostname = "rt3",
+ .network =
+ "10.0.2.1/24",
+ .metric = 50,
+ .label = 4,
+ },
+ },
+ },
+ {
+ .hostname = "rt3",
+ .router_id = "3.3.3.3",
+ .label = 30,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt2",
+ .network =
+ "10.0.2.2/24",
+ .metric = 50,
+ .label = 5,
+ },
+ {
+ .hostname = "rt4",
+ .network =
+ "10.0.3.1/24",
+ .metric = 10,
+ .label = 6,
+ },
+ },
+ },
+ {
+ .hostname = "rt4",
+ .router_id = "4.4.4.4",
+ .label = 40,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt3",
+ .network =
+ "10.0.3.2/24",
+ .metric = 10,
+ .label = 7,
+ },
+ {
+ .hostname = "rt1",
+ .network =
+ "10.0.4.2/24",
+ .metric = 10,
+ .label = 8,
+ },
+ },
+ },
+ },
+};
+
+/*
+ * +---------+ +---------+
+ * | | | |
+ * | RT1 |eth-rt4 eth-rt1| RT4 |
+ * | 1.1.1.1 +---------------------+ 4.4.4.4 |
+ * | | 10.0.4.0/24 | |
+ * +---------+ +---------+
+ * |eth+rt2 eth-rt3|
+ * | |
+ * |10.0.1.0/24 |
+ * | 10.0.3.0/24|
+ * |eth-rt1 eth-rt4|
+ * +---------+ +---------+
+ * | |eth-rt3 eth-rt2| |
+ * | RT2 +---------------------+ RT3 |
+ * | 2.2.2.2 | 10.0.2.0/24 | 3.3.3.3 |
+ * | | | |
+ * +---------+ +---------+
+ *
+ * Weights:
+ * - clockwise: 10
+ * - counterclockwise: 40
+ *
+ * This is an example where 3 (!) labels are needed for the protected
+ * link RT1<->RT2, e.g. the subnet 10.0.1.0/24, to reach RT4.
+ *
+ * Because the initial P and Q spaces will not be overlapping or
+ * adjacent for this case the TI-LFA will be applied recursively.
+ */
+struct ospf_topology topo5 = {
+ .nodes =
+ {
+ {
+ .hostname = "rt1",
+ .router_id = "1.1.1.1",
+ .label = 10,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt2",
+ .network =
+ "10.0.1.1/24",
+ .metric = 40,
+ .label = 1,
+ },
+ {
+ .hostname = "rt4",
+ .network =
+ "10.0.4.1/24",
+ .metric = 10,
+ .label = 2,
+ },
+ },
+ },
+ {
+ .hostname = "rt2",
+ .router_id = "2.2.2.2",
+ .label = 20,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt1",
+ .network =
+ "10.0.1.2/24",
+ .metric = 10,
+ .label = 3,
+ },
+ {
+ .hostname = "rt3",
+ .network =
+ "10.0.2.1/24",
+ .metric = 40,
+ .label = 4,
+ },
+ },
+ },
+ {
+ .hostname = "rt3",
+ .router_id = "3.3.3.3",
+ .label = 30,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt2",
+ .network =
+ "10.0.2.2/24",
+ .metric = 10,
+ .label = 5,
+ },
+ {
+ .hostname = "rt4",
+ .network =
+ "10.0.3.1/24",
+ .metric = 40,
+ .label = 6,
+ },
+ },
+ },
+ {
+ .hostname = "rt4",
+ .router_id = "4.4.4.4",
+ .label = 40,
+ .adjacencies =
+ {
+ {
+ .hostname = "rt3",
+ .network =
+ "10.0.3.2/24",
+ .metric = 10,
+ .label = 7,
+ },
+ {
+ .hostname = "rt1",
+ .network =
+ "10.0.4.2/24",
+ .metric = 40,
+ .label = 8,
+ },
+ },
+ },
+ },
+};
diff --git a/tests/pytest.ini b/tests/pytest.ini
new file mode 100644
index 0000000..3c436ed
--- /dev/null
+++ b/tests/pytest.ini
@@ -0,0 +1,2 @@
+[pytest]
+norecursedirs = topotests
diff --git a/tests/runtests.py b/tests/runtests.py
new file mode 100644
index 0000000..4677796
--- /dev/null
+++ b/tests/runtests.py
@@ -0,0 +1,6 @@
+import pytest
+import sys
+import os
+
+sys.path.append(os.path.join(os.path.dirname(__file__), "helpers", "python"))
+raise SystemExit(pytest.main(sys.argv[1:]))
diff --git a/tests/subdir.am b/tests/subdir.am
new file mode 100644
index 0000000..ab322f7
--- /dev/null
+++ b/tests/subdir.am
@@ -0,0 +1,76 @@
+#
+# tests
+#
+
+#
+# *sigh* - there is no way to get CPPFLAGS or CFLAGS for a group of files :(
+#
+
+TESTS_CPPFLAGS = $(AM_CPPFLAGS) \
+ -I$(top_srcdir)/tests/helpers/c \
+ -I$(top_builddir)/tests/helpers/c \
+ # end
+TESTS_CFLAGS = \
+ $(AC_CFLAGS) \
+ $(LIBYANG_CFLAGS) \
+ $(SAN_FLAGS) \
+ # end
+# note no -Werror
+
+TESTS_CXXFLAGS = \
+ $(AC_CXXFLAGS) \
+ $(LIBYANG_CFLAGS) \
+ $(SAN_FLAGS) \
+ # end
+# note no -Werror
+
+ALL_TESTS_LDADD = lib/libfrr.la $(LIBCAP)
+
+EXTRA_DIST += \
+ tests/runtests.py \
+ tests/helpers/python/frrsix.py \
+ tests/helpers/python/frrtest.py \
+ # end
+
+check_PROGRAMS =
+PYTEST_IGNORE =
+
+.PHONY: tests/tests.xml
+tests/tests.xml: $(check_PROGRAMS)
+ ( cd tests; $(PYTHON) ../$(srcdir)/tests/runtests.py --junitxml=tests.xml -v ../$(srcdir)/tests $(PYTEST_IGNORE); )
+check: tests/tests.xml
+
+clean-local: clean-tests
+.PHONY: clean-tests
+clean-tests:
+ -rm -f tests/tests.xml
+
+
+# CHEAT SHEET:
+#
+### conditional (if needed) - ONLY for "check_PROGRAMS +=" line!
+# if DAEMON
+# check_PROGRAMS += tests/daemon/test_foo
+# endif
+### CFLAGS/CPPFLAGS/LDADD as usual, extend on top of TESTS_XYZFLAGS
+# tests_daemon_test_foo_CFLAGS = $(TESTS_CFLAGS)
+# tests_daemon_test_foo_CPPFLAGS = $(TESTS_CPPFLAGS)
+# tests_daemon_test_foo_LDADD = $(ALL_TESTS_LDADD)
+# tests_daemon_test_foo_SOURCES = tests/daemon/test_foo.c
+### don't forget "nodist_" for autogenerated source files, & add to CLEANFILES
+# nodist_tests_daemon_test_foo_SOURCES = tests/daemon/test_foo_autogen.c
+# CLEANFILES += tests/daemon/test_foo_autogen.c
+### clippy_scan works normally
+# clippy_scan += tests/daemon/test_foo.c
+### header files for tests go into "noinst_HEADERS"
+# noinst_HEADERS += tests/daemon/foo.h
+### all python scripts & auxiliary files are added into EXTRA_DIST
+# EXTRA_DIST += tests/daemon/test_foo.py
+#
+
+include tests/bgpd/subdir.am
+include tests/isisd/subdir.am
+include tests/ospfd/subdir.am
+include tests/ospf6d/subdir.am
+include tests/zebra/subdir.am
+include tests/lib/subdir.am
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..a2f3d3b
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ip_nht.ref
@@ -0,0 +1,74 @@
+VRF default:
+ Resolve via default: on
+1.1.1.1
+ resolved via static
+ is directly connected, r1-eth1 (vrf default), weight 1
+ Client list: pbr(fd XX)
+1.1.1.2
+ resolved via static
+ is directly connected, r1-eth2 (vrf default), weight 1
+ Client list: pbr(fd XX)
+1.1.1.3
+ resolved via static
+ is directly connected, r1-eth3 (vrf default), weight 1
+ Client list: pbr(fd XX)
+1.1.1.4
+ resolved via static
+ is directly connected, r1-eth4 (vrf default), weight 1
+ Client list: pbr(fd XX)
+1.1.1.5
+ resolved via static
+ is directly connected, r1-eth5 (vrf default), weight 1
+ Client list: pbr(fd XX)
+1.1.1.6
+ resolved via static
+ is directly connected, r1-eth6 (vrf default), weight 1
+ Client list: pbr(fd XX)
+1.1.1.7
+ resolved via static
+ is directly connected, r1-eth7 (vrf default), weight 1
+ Client list: pbr(fd XX)
+1.1.1.8
+ resolved via static
+ is directly connected, r1-eth8 (vrf default), weight 1
+ 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 (vrf default)
+ Client list: static(fd XX)
+192.168.0.4
+ resolved via connected
+ is directly connected, r1-eth0 (vrf default)
+ Client list: static(fd XX)
+192.168.7.10
+ resolved via connected
+ is directly connected, r1-eth7 (vrf default)
+ Client list: bgp(fd XX)
+192.168.7.20(Connected)
+ resolved via connected
+ is directly connected, r1-eth7 (vrf default)
+ 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..100a36a
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ipv6_nht.ref
@@ -0,0 +1,15 @@
+VRF default:
+ Resolve via default: on
+fc00::2
+ resolved via connected
+ is directly connected, r1-eth0 (vrf default)
+ Client list: static(fd XX)
+fc00:0:0:8::1000
+ resolved via connected
+ is directly connected, r1-eth8 (vrf default)
+ Client list: bgp(fd XX)
+fc00:0:0:8::2000(Connected)
+ resolved via connected
+ is directly connected, r1-eth8 (vrf default)
+ Client list: bgp(fd XX)
+
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..33c2650
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ospf6d.conf
@@ -0,0 +1,20 @@
+log file ospf6d.log
+!
+! debug ospf6 lsa unknown
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+!
+interface r1-eth4
+ ipv6 ospf6 hello-interval 1
+!
+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..9ce2f2e
--- /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..61af44a
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ospfd.conf
@@ -0,0 +1,25 @@
+log file ospfd.log
+!
+! debug ospf event
+! debug ospf zebra
+!
+!
+interface r1-eth0
+ ip ospf hello-interval 1
+ ip ospf dead-interval 5
+!
+interface r1-eth3
+ ip ospf hello-interval 1
+ ip ospf dead-interval 5
+!
+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..b2e8de5
--- /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..7bee704
--- /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..31071e7
--- /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..53c4793
--- /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..fe3f072
--- /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..363b4f5
--- /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..8c3229b
--- /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..7e28f04
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref
@@ -0,0 +1,26 @@
+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 1s, Dead 5s, Wait 5s, Retransmit 5
+ Hello due in XX.XXXs
+ Neighbor Count is 0, Adjacent neighbor count is 0
+ Graceful Restart hello delay: 10s
+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 1s, Dead 5s, Wait 5s, Retransmit 5
+ Hello due in XX.XXXs
+ Neighbor Count is 0, Adjacent neighbor count is 0
+ Graceful Restart hello delay: 10s
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..8659700
--- /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, Padding: yes
+ 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, Padding: yes
+ 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..c319477
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py
@@ -0,0 +1,1724 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_all_protocol_startup.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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
+from lib.common_config import (
+ required_linux_kernel_version,
+)
+
+from lib.topolog import logger
+import json
+
+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("mgmtd", "%s/r%s/zebra.conf" % (thisDir, i))
+ 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)
+
+ 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")
+
+ 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__))
+
+ # We need loopback to have a link local so it always is the
+ # "selected" router for fe80::/64 when we static compare below.
+ print("Adding link-local to loopback for stable results")
+ cmd = (
+ "mac=`cat /sys/class/net/lo/address`; echo lo: $mac;"
+ " [ -z \"$mac\" ] && continue; IFS=':'; set $mac; unset IFS;"
+ " ip address add dev lo scope link"
+ " fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64"
+ )
+ net["r1"].cmd_raises(cmd)
+
+ print("\n\n** Waiting for protocols convergence")
+ print("******************************************\n")
+
+ # Not really implemented yet - just sleep 60 secs for now
+ sleep(5)
+
+ # 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_resilient_nexthop_group():
+ net = get_topogen().net
+
+ result = required_linux_kernel_version("5.19")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met, kernel version should be >= 5.19")
+
+ net["r1"].cmd(
+ 'vtysh -c "conf" -c "nexthop-group resilience" -c "resilient buckets 64 idle-timer 128 unbalanced-timer 256" -c "nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.2 r1-eth2 onlink"'
+ )
+
+ output = net["r1"].cmd('vtysh -c "show nexthop-group rib sharp"')
+ buckets = re.findall(r"Buckets", output)
+
+ output = net["r1"].cmd('vtysh -c "show nexthop-group rib sharp json"')
+
+ joutput = json.loads(output)
+
+ # Use the json output and collect the nhg id from it
+
+ for nhgid in joutput:
+ n = joutput[nhgid]
+ if "buckets" in n:
+ break
+
+ verify_nexthop_group(int(nhgid))
+ assert len(buckets) == 1, "Resilient NHG not created in zebra"
+
+
+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..690786a
--- /dev/null
+++ b/tests/topotests/analyze.py
@@ -0,0 +1,433 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# July 9 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C.
+#
+import argparse
+import atexit
+import logging
+import os
+import re
+import subprocess
+import sys
+import tempfile
+from collections import OrderedDict
+
+import xmltodict
+
+
+def get_range_list(rangestr):
+ result = []
+ for e in rangestr.split(","):
+ e = e.strip()
+ if not e:
+ continue
+ if e.find("-") == -1:
+ result.append(int(e))
+ else:
+ start, end = e.split("-")
+ result.extend(list(range(int(start), int(end) + 1)))
+ return result
+
+
+def dict_range_(dct, rangestr, dokeys):
+ keys = list(dct.keys())
+ if not rangestr or rangestr == "all":
+ for key in keys:
+ if dokeys:
+ yield key
+ else:
+ yield dct[key]
+ return
+
+ dlen = len(keys)
+ for index in get_range_list(rangestr):
+ if index >= dlen:
+ break
+ key = keys[index]
+ if dokeys:
+ yield key
+ else:
+ yield dct[key]
+
+
+def dict_range_keys(dct, rangestr):
+ return dict_range_(dct, rangestr, True)
+
+
+def dict_range_values(dct, rangestr):
+ return dict_range_(dct, rangestr, False)
+
+
+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 "@name" not in testcase:
+ tcname = fname
+ else:
+ tcname = fname + "::" + testcase["@name"]
+ found_files[tcname] = testcase
+ return found_files
+
+
+def search_testcase(testcase, regexp):
+ for key, val in testcase.items():
+ if regexp.search(str(val)):
+ return True
+ return False
+
+
+def dump_testcase(testcase):
+ 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-xml",
+ action="store_true",
+ help=(
+ "Move [container:]/tmp/topotests/topotests.xml "
+ "to --results value if --results does not exist yet"
+ ),
+ )
+ parser.add_argument(
+ "-A",
+ "--save",
+ action="store_true",
+ help=(
+ "Move [container:]/tmp/topotests{,.xml} "
+ "to --results value if --results does not exist yet"
+ ),
+ )
+ parser.add_argument(
+ "-C",
+ "--container",
+ help="specify docker/podman container of the run",
+ )
+ parser.add_argument(
+ "--use-podman",
+ action="store_true",
+ help="Use `podman` instead of `docker` for saving container data",
+ )
+ parser.add_argument(
+ "-S",
+ "--select",
+ help=(
+ "select results combination of letters: "
+ "'e'rrored 'f'ailed 'p'assed 's'kipped. "
+ "Default is 'fe', unless --search or --time which default to 'efps'"
+ ),
+ )
+ parser.add_argument(
+ "-R",
+ "--search",
+ help=(
+ "filter results to those which match a regex. "
+ "All test text is search unless restricted by --errmsg or --errtext"
+ ),
+ )
+ 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="select testcase at given ordinal from the enumerated list"
+ )
+ 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(
+ "--full", action="store_true", help="print all logging for selected testcases"
+ )
+ 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.save_xml:
+ logging.critical("Only one of --save or --save-xml allowed")
+ sys.exit(1)
+
+ scount = bool(args.save) + bool(args.save_xml)
+
+ #
+ # Saving/Archiving results
+ #
+
+ docker_bin = "podman" if args.use_podman else "docker"
+ contid = ""
+ if args.container:
+ # check for container existence
+ contid = args.container
+ try:
+ # p =
+ subprocess.run(
+ f"{docker_bin} inspect {contid}",
+ check=True,
+ shell=True,
+ errors="ignore",
+ capture_output=True,
+ )
+ except subprocess.CalledProcessError:
+ print(f"{docker_bin} container '{contid}' does not exist")
+ sys.exit(1)
+ # If you need container info someday...
+ # cont_info = json.loads(p.stdout)
+
+ cppath = "/tmp/topotests"
+ if args.save_xml or scount == 0:
+ cppath += "/topotests.xml"
+ if contid:
+ cppath = contid + ":" + cppath
+
+ tresfile = None
+
+ if scount and args.results and not os.path.exists(args.results):
+ if not contid:
+ if not os.path.exists(cppath):
+ print(f"'{cppath}' doesn't exist to save")
+ sys.exit(1)
+ if args.save_xml:
+ subprocess.run(["cp", cppath, args.results])
+ else:
+ subprocess.run(["mv", cppath, args.results])
+ else:
+ try:
+ subprocess.run(
+ f"{docker_bin} cp {cppath} {args.results}",
+ check=True,
+ shell=True,
+ errors="ignore",
+ capture_output=True,
+ )
+ except subprocess.CalledProcessError as error:
+ print(f"Can't {docker_bin} cp '{cppath}': %s", str(error))
+ sys.exit(1)
+
+ if "SUDO_USER" in os.environ:
+ subprocess.run(["chown", "-R", os.environ["SUDO_USER"], args.results])
+ elif not args.results:
+ # User doesn't want to save results just use them inplace
+ if not contid:
+ if not os.path.exists(cppath):
+ print(f"'{cppath}' doesn't exist")
+ sys.exit(1)
+ args.results = cppath
+ else:
+ tresfile, tresname = tempfile.mkstemp(
+ suffix=".xml", prefix="topotests-", text=True
+ )
+ atexit.register(lambda: os.unlink(tresname))
+ os.close(tresfile)
+ try:
+ subprocess.run(
+ f"{docker_bin} cp {cppath} {tresname}",
+ check=True,
+ shell=True,
+ errors="ignore",
+ capture_output=True,
+ )
+ except subprocess.CalledProcessError as error:
+ print(f"Can't {docker_bin} cp '{cppath}': %s", str(error))
+ sys.exit(1)
+ args.results = tresname
+
+ #
+ # Result option validation
+ #
+
+ count = 0
+ if args.errmsg:
+ count += 1
+ if args.errtext:
+ count += 1
+ if args.full:
+ count += 1
+ if count > 1:
+ logging.critical("Only one of --full, --errmsg or --errtext allowed")
+ sys.exit(1)
+
+ if args.time and count:
+ logging.critical("Can't use --full, --errmsg or --errtext with --time")
+ sys.exit(1)
+
+ if args.enumerate and (count or args.time or args.test):
+ logging.critical(
+ "Can't use --enumerate with --errmsg, --errtext, --full, --test or --time"
+ )
+ sys.exit(1)
+
+ results = {}
+ ttfiles = []
+
+ 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]
+
+ 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"]
+
+ search_re = re.compile(args.search) if args.search else None
+
+ if args.select is None:
+ if search_re or args.time:
+ args.select = "efsp"
+ else:
+ args.select = "fe"
+
+ 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 search_re:
+ found_files = {
+ k: v for k, v in found_files.items() if search_testcase(v, search_re)
+ }
+
+ if args.enumerate:
+ # print the selected test names with ordinal
+ print("\n".join(["{} {}".format(i, x) for i, x in enumerate(found_files)]))
+ elif args.test is None and count == 0 and not args.time:
+ # print the selected test names
+ print("\n".join([str(x) for x in found_files]))
+ else:
+ rangestr = args.test if args.test else "all"
+ for key in dict_range_keys(found_files, rangestr):
+ testcase = found_files[key]
+ if args.time:
+ text = testcase["@time"]
+ s = "{}: {}".format(text, key)
+ elif 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.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)
+
+ if args.summary:
+ print_summary(results, args)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/topotests/babel_topo1/r1/babeld.conf b/tests/topotests/babel_topo1/r1/babeld.conf
new file mode 100644
index 0000000..4058362
--- /dev/null
+++ b/tests/topotests/babel_topo1/r1/babeld.conf
@@ -0,0 +1,20 @@
+
+interface r1-eth0
+ babel hello-interval 1000
+ babel wired
+ babel update-interval 50
+!
+interface r1-eth1
+ babel hello-interval 1000
+ babel wired
+ babel update-interval 50
+!
+router babel
+ network r1-eth0
+ network r1-eth1
+ redistribute ipv4 connected
+ redistribute ipv6 connected
+!
+line vty
+!
+
diff --git a/tests/topotests/babel_topo1/r1/show_ip_route.json_ref b/tests/topotests/babel_topo1/r1/show_ip_route.json_ref
new file mode 100644
index 0000000..0d52b67
--- /dev/null
+++ b/tests/topotests/babel_topo1/r1/show_ip_route.json_ref
@@ -0,0 +1,80 @@
+{
+ "192.168.1.0/24":[
+ {
+ "prefix":"192.168.1.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.2.0/24":[
+ {
+ "prefix":"192.168.2.0/24",
+ "prefixLen":24,
+ "protocol":"babel",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"193.1.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1"
+ }
+ ]
+ }
+ ],
+ "192.168.3.0/24":[
+ {
+ "prefix":"192.168.3.0/24",
+ "protocol":"babel",
+ "selected":true,
+ "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":"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":"babel",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"193.1.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/babel_topo1/r1/zebra.conf b/tests/topotests/babel_topo1/r1/zebra.conf
new file mode 100644
index 0000000..5eda3e2
--- /dev/null
+++ b/tests/topotests/babel_topo1/r1/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+! debug zebra rib detail
+!
+hostname r1
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+ ipv6 address 192:168:1::1/64
+!
+interface r1-eth1
+ description to sw2 - babel interface
+ ip address 193.1.1.1/26
+ ipv6 address 193:1:1::1/64
+ no link-detect
+!
+ip forwarding
+ipv6 forwarding
+!
+!
+line vty
+!
+
diff --git a/tests/topotests/babel_topo1/r2/babeld.conf b/tests/topotests/babel_topo1/r2/babeld.conf
new file mode 100644
index 0000000..bae4e59
--- /dev/null
+++ b/tests/topotests/babel_topo1/r2/babeld.conf
@@ -0,0 +1,17 @@
+!
+interface r2-eth0
+ babel hello-interval 1000
+ babel wired
+ babel update-interval 50
+!
+interface r2-eth1
+ babel hello-interval 1000
+ babel wired
+ babel update-interval 50
+!
+router babel
+ network r2-eth0
+ network r2-eth1
+ redistribute ipv4 connected
+ redistribute ipv6 connected
+!
diff --git a/tests/topotests/babel_topo1/r2/show_ip_route.json_ref b/tests/topotests/babel_topo1/r2/show_ip_route.json_ref
new file mode 100644
index 0000000..ff3f6ab
--- /dev/null
+++ b/tests/topotests/babel_topo1/r2/show_ip_route.json_ref
@@ -0,0 +1,80 @@
+{
+ "192.168.1.0/24":[
+ {
+ "prefix":"192.168.1.0/24",
+ "protocol":"babel",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"193.1.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.2.0/24":[
+ {
+ "prefix":"192.168.2.0/24",
+ "protocol":"babel",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"193.1.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.3.0/24":[
+ {
+ "prefix":"192.168.3.0/24",
+ "protocol":"babel",
+ "selected":true,
+ "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":"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":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/babel_topo1/r2/zebra.conf b/tests/topotests/babel_topo1/r2/zebra.conf
new file mode 100644
index 0000000..301a8b2
--- /dev/null
+++ b/tests/topotests/babel_topo1/r2/zebra.conf
@@ -0,0 +1,23 @@
+log file zebra.log
+!
+hostname r2
+!
+interface r2-eth0
+ description to sw2 - babel interface
+ ip address 193.1.1.2/26
+ ipv6 address 193:1:1::2/64
+ no link-detect
+!
+interface r2-eth1
+ description to sw3 - babel interface
+ ip address 193.1.2.1/24
+ ipv6 address 193:1:2::1/64
+ no link-detect
+!
+ip forwarding
+ipv6 forwarding
+!
+!
+line vty
+!
+
diff --git a/tests/topotests/babel_topo1/r3/babeld.conf b/tests/topotests/babel_topo1/r3/babeld.conf
new file mode 100644
index 0000000..e10e5aa
--- /dev/null
+++ b/tests/topotests/babel_topo1/r3/babeld.conf
@@ -0,0 +1,16 @@
+!
+interface r3-eth0
+ babel hello-interval 1000
+ babel wired
+ babel update-interval 50
+!
+interface r3-eth1
+ babel hello-interval 1000
+ babel wired
+ babel update-interval 50
+!
+router babel
+ network r3-eth0
+ network r3-eth1
+ redistribute ipv4 connected
+ redistribute ipv4 static
diff --git a/tests/topotests/babel_topo1/r3/show_ip_route.json_ref b/tests/topotests/babel_topo1/r3/show_ip_route.json_ref
new file mode 100644
index 0000000..7fe66fa
--- /dev/null
+++ b/tests/topotests/babel_topo1/r3/show_ip_route.json_ref
@@ -0,0 +1,81 @@
+{
+ "192.168.1.0/24":[
+ {
+ "prefix":"192.168.1.0/24",
+ "protocol":"babel",
+ "selected":true,
+ "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,
+ "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":"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":"babel",
+ "selected":true,
+ "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":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/babel_topo1/r3/zebra.conf b/tests/topotests/babel_topo1/r3/zebra.conf
new file mode 100644
index 0000000..34e8c62
--- /dev/null
+++ b/tests/topotests/babel_topo1/r3/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+!
+hostname r3
+!
+interface r3-eth0
+ description to sw4 - Stub interface
+ ip address 192.168.3.1/24
+ ipv6 address 192:168:3::1/64
+ no link-detect
+!
+interface r3-eth1
+ description to sw3 - RIPv2 interface
+ ip address 193.1.2.2/24
+ ipv6 address 193:1:2::2/64
+ no link-detect
+!
+ip route 192.168.2.0/24 192.168.3.10
+ipv6 route 192:168:2::0/64 192:168:3::10
+!
+ip forwarding
+ipv6 forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/babel_topo1/test_babel_topo1.py b/tests/topotests/babel_topo1/test_babel_topo1.py
new file mode 100644
index 0000000..decf0c2
--- /dev/null
+++ b/tests/topotests/babel_topo1/test_babel_topo1.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_babel_topo1.py
+#
+# Copyright (c) 2017 by
+# Cumulus Networks, Inc.
+# Donald Sharp
+#
+
+"""
+test_babel_topo1.py: Testing BABEL
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+from functools import partial
+
+pytestmark = [pytest.mark.babeld]
+
+# 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 BABEL
+ # switch 2 switch is for connection to BABEL router
+ switch = tgen.add_switch("sw2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # switch 4 is stub on remote BABEL router
+ switch = tgen.add_switch("sw4")
+ switch.add_link(tgen.gears["r3"])
+
+ # switch 3 is between BABEL 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_BABEL, os.path.join(CWD, "{}/babeld.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(10, "Waiting for BABEL convergence")
+
+
+def runit(router, assertmsg, cmd, expfile):
+ logger.info(expfile)
+
+ # Read expected result from file
+ expected = json.loads(open(expfile).read())
+
+ test_func = partial(topotest.router_json_cmp, router, cmd, expected)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result 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:
+ assertmsg = "Zebra IPv4 Routing Table verification failed for router {}".format(
+ router.name
+ )
+ refTableFile = "{}/{}/show_ip_route.json_ref".format(CWD, router.name)
+ runit(router, assertmsg, "show ip route json", refTableFile)
+
+
+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("babeld")
+ if log:
+ logger.error("BABELd 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/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..f8ad1f3
--- /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 3 10
+ 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..42953a0
--- /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 3 10
+ 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..906687d
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py
@@ -0,0 +1,234 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bfd_bgp_cbit_topo3.py
+#
+# Copyright (c) 2019 6WIND
+#
+
+"""
+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)
+
+ r1 = tgen.gears["r1"]
+ expected = { "as":101, "peers":{ "2001:db8:4::1": { "state":"Established" } } }
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv6 uni summ json", expected)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg ='"r1" has not established bgp peering yet'
+ assert result is None, assertmsg
+
+ #assert False
+ 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..5ae236d
--- /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 10
+ isis bfd
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 10
+ 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..ff99f18
--- /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 10
+ isis bfd
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 10
+!
+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..2ad1b9c
--- /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 10
+ isis network point-to-point
+ isis bfd
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 10
+!
+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..e170c19
--- /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 10
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 10
+!
+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..3caaca7
--- /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 10
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 10
+!
+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..09e6914
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/test_bfd_isis_topo1.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bfd_isis_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..72238cc
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/ospfd.conf
@@ -0,0 +1,31 @@
+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
+ 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..c5f4262
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt2/ospfd.conf
@@ -0,0 +1,29 @@
+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
+ 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..e487bdd
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt3/ospfd.conf
@@ -0,0 +1,29 @@
+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
+ 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..560904e
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt4/ospfd.conf
@@ -0,0 +1,28 @@
+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
+ 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..77f5445
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt5/ospfd.conf
@@ -0,0 +1,28 @@
+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
+ 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..b9e8b73
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/test_bfd_ospf_topo1.py
@@ -0,0 +1,260 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bfd_ospf_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..78841b3
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bfd_profiles_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..1b77493
--- /dev/null
+++ b/tests/topotests/bfd_topo1/test_bfd_topo1.py
@@ -0,0 +1,213 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bfd_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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=5, 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..636dbf3
--- /dev/null
+++ b/tests/topotests/bfd_topo2/test_bfd_topo2.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bfd_topo2.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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/bfd-static-down.json b/tests/topotests/bfd_topo3/r3/bfd-static-down.json
new file mode 100644
index 0000000..60752d3
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r3/bfd-static-down.json
@@ -0,0 +1,12 @@
+{
+ "path-list": {
+ "ipv4-multicast": [],
+ "ipv4-unicast": [],
+ "ipv6-unicast": [
+ {
+ "prefix": "2001:db8:5::\/64",
+ "vrf": "default",
+ "installed": false
+ }
+ ]
+ }}
diff --git a/tests/topotests/bfd_topo3/r3/bfd-static.json b/tests/topotests/bfd_topo3/r3/bfd-static.json
new file mode 100644
index 0000000..c65060c
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r3/bfd-static.json
@@ -0,0 +1,13 @@
+{
+ "path-list": {
+ "ipv4-multicast": [],
+ "ipv4-unicast": [],
+ "ipv6-unicast": [
+ {
+ "prefix": "2001:db8:5::\/64",
+ "vrf": "default",
+ "installed": true
+ }
+ ]
+ }
+}
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/staticd.conf b/tests/topotests/bfd_topo3/r3/staticd.conf
new file mode 100644
index 0000000..44f91f3
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r3/staticd.conf
@@ -0,0 +1 @@
+ipv6 route 2001:db8:5::/64 2001:db8:4::3 bfd multi-hop profile slow-tx
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..4f71d75
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r4/bfd-peers.json
@@ -0,0 +1,90 @@
+[
+ {
+ "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,
+ "vrf": "default"
+ },
+ {
+ "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,
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "id": "*",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "192.168.4.3",
+ "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",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "id": "*",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "192.168.4.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",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd_topo3/r4/bfdd.conf b/tests/topotests/bfd_topo3/r4/bfdd.conf
new file mode 100644
index 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..bfad78a
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r4/bgpd.conf
@@ -0,0 +1,19 @@
+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
+ redistribute static
+ 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/staticd.conf b/tests/topotests/bfd_topo3/r4/staticd.conf
new file mode 100644
index 0000000..3b1c5bf
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r4/staticd.conf
@@ -0,0 +1,2 @@
+ip route 10.254.254.5/32 192.168.4.2 bfd profile slow-tx
+ip route 10.254.254.6/32 192.168.4.3 bfd profile slow-tx
diff --git a/tests/topotests/bfd_topo3/r4/zebra.conf b/tests/topotests/bfd_topo3/r4/zebra.conf
new file mode 100644
index 0000000..2574941
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r4/zebra.conf
@@ -0,0 +1,14 @@
+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
+!
+interface r4-eth1
+ ip address 192.168.4.1/24
+ ipv6 address 2001:db8:4::1/64
+!
diff --git a/tests/topotests/bfd_topo3/r5/bfd-peers.json b/tests/topotests/bfd_topo3/r5/bfd-peers.json
new file mode 100644
index 0000000..777b1dd
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r5/bfd-peers.json
@@ -0,0 +1,23 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "id": "*",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "192.168.4.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",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd_topo3/r5/bfdd.conf b/tests/topotests/bfd_topo3/r5/bfdd.conf
new file mode 100644
index 0000000..ec62d8d
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r5/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
+ minimum-ttl 250
+ !
+!
diff --git a/tests/topotests/bfd_topo3/r5/staticd.conf b/tests/topotests/bfd_topo3/r5/staticd.conf
new file mode 100644
index 0000000..9828cff
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r5/staticd.conf
@@ -0,0 +1,2 @@
+ip route 0.0.0.0/0 192.168.4.1
+ip route 10.254.254.4/32 192.168.4.1 bfd profile slow-tx
diff --git a/tests/topotests/bfd_topo3/r5/zebra.conf b/tests/topotests/bfd_topo3/r5/zebra.conf
new file mode 100644
index 0000000..f84ce7e
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r5/zebra.conf
@@ -0,0 +1,10 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.5/32
+!
+interface r5-eth0
+ ip address 192.168.4.2/24
+ ipv6 address 2001:db8:4::2/64
+!
diff --git a/tests/topotests/bfd_topo3/r6/bfd-peers.json b/tests/topotests/bfd_topo3/r6/bfd-peers.json
new file mode 100644
index 0000000..4de451d
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r6/bfd-peers.json
@@ -0,0 +1,46 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "id": "*",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "192.168.4.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",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "id": "*",
+ "local": "2001:db8:4::3",
+ "minimum-ttl": 2,
+ "multihop": true,
+ "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",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd_topo3/r6/bfd-static-down.json b/tests/topotests/bfd_topo3/r6/bfd-static-down.json
new file mode 100644
index 0000000..4dadff2
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r6/bfd-static-down.json
@@ -0,0 +1,19 @@
+{
+ "path-list": {
+ "ipv4-multicast": [],
+ "ipv4-unicast": [
+ {
+ "installed": true,
+ "prefix": "10.254.254.4/32",
+ "vrf": "default"
+ }
+ ],
+ "ipv6-unicast": [
+ {
+ "prefix": "2001:db8:1::\/64",
+ "vrf": "default",
+ "installed": false
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_topo3/r6/bfd-static.json b/tests/topotests/bfd_topo3/r6/bfd-static.json
new file mode 100644
index 0000000..d042889
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r6/bfd-static.json
@@ -0,0 +1,19 @@
+{
+ "path-list": {
+ "ipv4-multicast": [],
+ "ipv4-unicast": [
+ {
+ "installed": true,
+ "prefix": "10.254.254.4/32",
+ "vrf": "default"
+ }
+ ],
+ "ipv6-unicast": [
+ {
+ "prefix": "2001:db8:1::\/64",
+ "vrf": "default",
+ "installed": true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_topo3/r6/bfdd.conf b/tests/topotests/bfd_topo3/r6/bfdd.conf
new file mode 100644
index 0000000..ec62d8d
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r6/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
+ minimum-ttl 250
+ !
+!
diff --git a/tests/topotests/bfd_topo3/r6/staticd.conf b/tests/topotests/bfd_topo3/r6/staticd.conf
new file mode 100644
index 0000000..28da508
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r6/staticd.conf
@@ -0,0 +1,5 @@
+ip route 0.0.0.0/0 192.168.4.1
+ip route 10.254.254.4/32 192.168.4.1 bfd profile slow-tx
+!
+ipv6 route 2001:db8:3::/64 2001:db8:4::1
+ipv6 route 2001:db8:1::/64 2001:db8:3::2 bfd multi-hop source 2001:db8:4::3 profile slow-tx
diff --git a/tests/topotests/bfd_topo3/r6/zebra.conf b/tests/topotests/bfd_topo3/r6/zebra.conf
new file mode 100644
index 0000000..b8f2ea4
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r6/zebra.conf
@@ -0,0 +1,10 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.6/32
+!
+interface r6-eth0
+ ip address 192.168.4.3/24
+ ipv6 address 2001:db8:4::3/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..8d18783
--- /dev/null
+++ b/tests/topotests/bfd_topo3/test_bfd_topo3.dot
@@ -0,0 +1,95 @@
+## 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,
+ ];
+ r5 [
+ shape=doubleoctagon
+ label="r5",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r6 [
+ shape=doubleoctagon
+ label="r6",
+ 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,
+ ];
+ sw4 [
+ shape=oval,
+ label="sw4\n192.168.4.0/24\n2001:db8:4::/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"];
+
+ r4 -- sw4 [label="eth1\n.1"];
+ r5 -- sw4 [label="eth0\n.2"];
+ r6 -- sw4 [label="eth0\n.3"];
+}
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..f100eae
--- /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..c0dc052
--- /dev/null
+++ b/tests/topotests/bfd_topo3/test_bfd_topo3.py
@@ -0,0 +1,248 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bfd_topo3.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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"),
+ "s4": ("r4", "r5", "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 = "{}/{}/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 = "{}/{}/staticd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_STATIC, 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 R1 <-> R5 convergence.
+ expect_loopback_route("r1", "ip", "10.254.254.5/32", "bgp")
+ # Wait for R1 <-> R6 convergence.
+ expect_loopback_route("r1", "ip", "10.254.254.6/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 R2 <-> R5 convergence.
+ expect_loopback_route("r2", "ip", "10.254.254.5/32", "bgp")
+ # Wait for R2 <-> R6 convergence.
+ expect_loopback_route("r2", "ip", "10.254.254.6/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 R3 <-> R5 convergence.
+ expect_loopback_route("r3", "ip", "10.254.254.5/32", "bgp")
+ # Wait for R3 <-> R6 convergence.
+ expect_loopback_route("r3", "ip", "10.254.254.6/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")
+ # Wait for R4 <-> R5 convergence.
+ expect_loopback_route("r4", "ip", "10.254.254.5/32", "static")
+ # Wait for R4 <-> R6 convergence.
+ expect_loopback_route("r4", "ip", "10.254.254.6/32", "static")
+
+ # Wait for R5 <-> R6 convergence.
+ expect_loopback_route("r3", "ipv6", "2001:db8:5::/64", "static")
+ # Wait for R6 <-> R5 convergence.
+ expect_loopback_route("r6", "ipv6", "2001:db8:1::/64", "static")
+
+
+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")
+ expect_bfd_configuration("r5")
+ expect_bfd_configuration("r6")
+
+
+def test_static_route_monitoring():
+ "Test static route monitoring output."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("test BFD static route status")
+
+ def expect_static_bfd_output(router, filename):
+ "Load JSON file and compare with 'show bfd peer json'"
+ logger.info("waiting BFD configuration on router {}".format(router))
+ bfd_config = json.loads(
+ open("{}/{}/{}.json".format(CWD, router, filename)).read()
+ )
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show bfd static route json",
+ bfd_config,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assertmsg = '"{}" BFD static route status failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_static_bfd_output("r3", "bfd-static")
+ expect_static_bfd_output("r6", "bfd-static")
+
+ logger.info("Setting r4 link down ...")
+
+ tgen.gears["r4"].link_enable("r4-eth0", False)
+
+ expect_static_bfd_output("r3", "bfd-static-down")
+ expect_static_bfd_output("r6", "bfd-static-down")
+
+
+def test_expect_static_rib_removal():
+ "Test that route got removed from RIB (staticd and bgpd)."
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def expect_route_missing(router, iptype, route):
+ "Wait until route is present on RIB for protocol."
+ logger.info("waiting route {} to disapear in {}".format(route, router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show {} route json".format(iptype),
+ {route: None},
+ )
+ rv, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_route_missing("r1", "ip", "10.254.254.5/32")
+ expect_route_missing("r2", "ip", "10.254.254.5/32")
+ expect_route_missing("r3", "ip", "10.254.254.5/32")
+ expect_route_missing("r3", "ipv6", "2001:db8:5::/64")
+ expect_route_missing("r6", "ipv6", "2001:db8:1::/64")
+
+
+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..a532f3a
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py
@@ -0,0 +1,267 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# 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
+#
+
+"""
+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..30f4a2f
--- /dev/null
+++ b/tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# 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
+#
+
+"""
+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_accept_own/__init__.py b/tests/topotests/bgp_accept_own/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/__init__.py
diff --git a/tests/topotests/bgp_accept_own/ce1/bgpd.conf b/tests/topotests/bgp_accept_own/ce1/bgpd.conf
new file mode 100644
index 0000000..44f95b9
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/ce1/bgpd.conf
@@ -0,0 +1,12 @@
+!
+!debug bgp updates
+!
+router bgp 65010
+ 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
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_accept_own/ce1/zebra.conf b/tests/topotests/bgp_accept_own/ce1/zebra.conf
new file mode 100644
index 0000000..7863ae1
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/ce1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface ce1-eth0
+ ip address 192.168.1.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_accept_own/ce2/bgpd.conf b/tests/topotests/bgp_accept_own/ce2/bgpd.conf
new file mode 100644
index 0000000..d60fdcf
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/ce2/bgpd.conf
@@ -0,0 +1,12 @@
+!
+!debug bgp updates
+!
+router bgp 65020
+ 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
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_accept_own/ce2/zebra.conf b/tests/topotests/bgp_accept_own/ce2/zebra.conf
new file mode 100644
index 0000000..829967e
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/ce2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.2/32
+!
+interface ce2-eth0
+ ip address 192.168.2.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_accept_own/pe1/bgpd.conf b/tests/topotests/bgp_accept_own/pe1/bgpd.conf
new file mode 100644
index 0000000..15466b4
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/pe1/bgpd.conf
@@ -0,0 +1,50 @@
+!
+!debug bgp updates
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp nht
+!
+router bgp 65001
+ bgp router-id 10.10.10.10
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 10.10.10.101 remote-as internal
+ neighbor 10.10.10.101 update-source 10.10.10.10
+ neighbor 10.10.10.101 timers 1 3
+ neighbor 10.10.10.101 timers connect 1
+ address-family ipv4 vpn
+ neighbor 10.10.10.101 activate
+ neighbor 10.10.10.101 attribute-unchanged
+ exit-address-family
+!
+router bgp 65001 vrf Customer
+ 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
+ redistribute connected
+ label vpn export 10
+ rd vpn export 192.168.1.2:2
+ rt vpn import 192.168.1.2:2
+ rt vpn export 192.168.1.2:2
+ export vpn
+ import vpn
+ exit-address-family
+!
+router bgp 65001 vrf Service
+ 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
+ label vpn export 20
+ rd vpn export 192.168.2.2:2
+ rt vpn import 192.168.2.2:2
+ rt vpn export 192.168.2.2:2
+ export vpn
+ import vpn
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_accept_own/pe1/ldpd.conf b/tests/topotests/bgp_accept_own/pe1/ldpd.conf
new file mode 100644
index 0000000..7c1ea33
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/pe1/ldpd.conf
@@ -0,0 +1,12 @@
+mpls ldp
+ router-id 10.10.10.10
+ !
+ address-family ipv4
+ discovery transport-address 10.10.10.10
+ !
+ interface pe1-eth2
+ exit
+ !
+ exit-address-family
+ !
+exit
diff --git a/tests/topotests/bgp_accept_own/pe1/ospfd.conf b/tests/topotests/bgp_accept_own/pe1/ospfd.conf
new file mode 100644
index 0000000..1a5e1a0
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/pe1/ospfd.conf
@@ -0,0 +1,7 @@
+interface pe1-eth2
+ 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_accept_own/pe1/zebra.conf b/tests/topotests/bgp_accept_own/pe1/zebra.conf
new file mode 100644
index 0000000..2b7aefa
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/pe1/zebra.conf
@@ -0,0 +1,18 @@
+!
+interface lo
+ ip address 10.10.10.10/32
+!
+interface pe1-eth0 vrf Customer
+ ip address 192.168.1.2/24
+!
+interface pe1-eth1 vrf Service
+ ip address 192.168.2.2/24
+!
+interface pe1-eth2
+ ip address 10.0.1.1/24
+!
+interface pe1-eth3 vrf Customer
+ ip address 192.0.2.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_accept_own/rr1/bgpd.conf b/tests/topotests/bgp_accept_own/rr1/bgpd.conf
new file mode 100644
index 0000000..ad0ee3e
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/rr1/bgpd.conf
@@ -0,0 +1,25 @@
+!
+!debug bgp updates
+!
+router bgp 65001
+ bgp router-id 10.10.10.101
+ bgp route-reflector allow-outbound-policy
+ 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.101
+ neighbor 10.10.10.10 timers 1 3
+ neighbor 10.10.10.10 timers connect 1
+ address-family ipv4 vpn
+ neighbor 10.10.10.10 activate
+ neighbor 10.10.10.10 route-reflector-client
+ neighbor 10.10.10.10 route-map pe1 out
+ exit-address-family
+!
+route-map pe1 permit 10
+ set extcommunity rt 192.168.1.2:2 192.168.2.2:2
+ set community 65001:111 accept-own additive
+ set ip next-hop unchanged
+route-map pe1 permit 20
+exit
+!
diff --git a/tests/topotests/bgp_accept_own/rr1/ldpd.conf b/tests/topotests/bgp_accept_own/rr1/ldpd.conf
new file mode 100644
index 0000000..0369901
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/rr1/ldpd.conf
@@ -0,0 +1,12 @@
+mpls ldp
+ router-id 10.10.10.101
+ !
+ address-family ipv4
+ discovery transport-address 10.10.10.101
+ !
+ interface rr1-eth0
+ exit
+ !
+ exit-address-family
+ !
+exit
diff --git a/tests/topotests/bgp_accept_own/rr1/ospfd.conf b/tests/topotests/bgp_accept_own/rr1/ospfd.conf
new file mode 100644
index 0000000..b598246
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/rr1/ospfd.conf
@@ -0,0 +1,7 @@
+interface rr1-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+!
+router ospf
+ router-id 10.10.10.101
+ network 0.0.0.0/0 area 0
diff --git a/tests/topotests/bgp_accept_own/rr1/zebra.conf b/tests/topotests/bgp_accept_own/rr1/zebra.conf
new file mode 100644
index 0000000..aa3f633
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/rr1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 10.10.10.101/32
+!
+interface rr1-eth0
+ ip address 10.0.1.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_accept_own/test_bgp_accept_own.py b/tests/topotests/bgp_accept_own/test_bgp_accept_own.py
new file mode 100644
index 0000000..d294da0
--- /dev/null
+++ b/tests/topotests/bgp_accept_own/test_bgp_accept_own.py
@@ -0,0 +1,216 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+
+"""
+
+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("ce1")
+ tgen.add_router("ce2")
+ tgen.add_router("pe1")
+ tgen.add_router("rr1")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["ce1"])
+ switch.add_link(tgen.gears["pe1"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["ce2"])
+ switch.add_link(tgen.gears["pe1"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["pe1"])
+ switch.add_link(tgen.gears["rr1"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["pe1"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ pe1 = tgen.gears["pe1"]
+ rr1 = tgen.gears["rr1"]
+
+ pe1.run("ip link add Customer type vrf table 1001")
+ pe1.run("ip link set up dev Customer")
+ pe1.run("ip link set pe1-eth0 master Customer")
+ pe1.run("ip link add Service type vrf table 1002")
+ pe1.run("ip link set up dev Service")
+ pe1.run("ip link set pe1-eth1 master Service")
+ pe1.run("ip link set pe1-eth3 master Customer")
+ pe1.run("sysctl -w net.mpls.conf.pe1-eth2.input=1")
+ rr1.run("sysctl -w net.mpls.conf.rr1-eth0.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_accept_own():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ pe1 = tgen.gears["pe1"]
+ ce2 = tgen.gears["ce2"]
+
+ step("Check if routes are not installed in PE1 from RR1 (due to ORIGINATOR_ID)")
+
+ def _bgp_check_received_routes_due_originator_id():
+ output = json.loads(pe1.vtysh_cmd("show bgp ipv4 vpn summary json"))
+ expected = {"peers": {"10.10.10.101": {"pfxRcd": 0, "pfxSnt": 5}}}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_received_routes_due_originator_id)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Failed, received routes from RR1 regardless ORIGINATOR_ID"
+
+ step("Enable ACCEPT_OWN for RR1")
+
+ pe1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65001
+ address-family ipv4 vpn
+ neighbor 10.10.10.101 accept-own
+ """
+ )
+
+ step("Check if we received routes due to ACCEPT_OWN from RR1")
+
+ def _bgp_check_received_routes_with_modified_rts():
+ output = json.loads(pe1.vtysh_cmd("show bgp ipv4 vpn summary json"))
+ expected = {"peers": {"10.10.10.101": {"pfxRcd": 5, "pfxSnt": 5}}}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_received_routes_with_modified_rts)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert (
+ result is None
+ ), "Failed, didn't receive routes from RR1 with ACCEPT_OWN enabled"
+
+ step(
+ "Check if 172.16.255.1/32 is imported into vrf Service due to modified RT list at RR1"
+ )
+
+ def _bgp_check_received_routes_with_changed_rts():
+ output = json.loads(
+ pe1.vtysh_cmd("show bgp vrf Service ipv4 unicast 172.16.255.1/32 json")
+ )
+ expected = {
+ "paths": [
+ {
+ "community": {"string": "65001:111"},
+ "extendedCommunity": {
+ "string": "RT:192.168.1.2:2 RT:192.168.2.2:2"
+ },
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_received_routes_with_changed_rts)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert (
+ result is None
+ ), "Failed, routes are not imported from RR1 with modified RT list"
+
+ step("Check if 192.0.2.0/24 is imported to vrf Service from vrf Customer")
+
+ def _bgp_check_imported_local_routes_from_vrf_service():
+ output = json.loads(
+ pe1.vtysh_cmd("show ip route vrf Service 192.0.2.0/24 json")
+ )
+ expected = {
+ "192.0.2.0/24": [
+ {
+ "vrfName": "Service",
+ "table": 1002,
+ "installed": True,
+ "selected": True,
+ "nexthops": [
+ {
+ "fib": True,
+ "vrf": "Customer",
+ "active": True,
+ }
+ ],
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_imported_local_routes_from_vrf_service)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert (
+ result is None
+ ), "Failed, didn't import local route 192.0.2.0/24 from vrf Customer to vrf Service"
+
+ step("Check if 172.16.255.1/32 is announced to CE2")
+
+ def _bgp_check_received_routes_from_pe():
+ output = json.loads(ce2.vtysh_cmd("show ip route 172.16.255.1/32 json"))
+ expected = {
+ "172.16.255.1/32": [
+ {
+ "protocol": "bgp",
+ "installed": True,
+ "nexthops": [{"ip": "192.168.2.2"}],
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_received_routes_from_pe)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Failed, didn't receive 172.16.255.1/32 from PE1"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_addpath_best_selected/__init__.py b/tests/topotests/bgp_addpath_best_selected/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/__init__.py
diff --git a/tests/topotests/bgp_addpath_best_selected/r1/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r1/bgpd.conf
new file mode 100644
index 0000000..ba10f7b
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/r1/bgpd.conf
@@ -0,0 +1,7 @@
+!
+router bgp 65001
+ timers bgp 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers connect 5
+!
diff --git a/tests/topotests/bgp_addpath_best_selected/r1/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r1/zebra.conf
new file mode 100644
index 0000000..b29940f
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_addpath_best_selected/r2/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r2/bgpd.conf
new file mode 100644
index 0000000..cdef611
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/r2/bgpd.conf
@@ -0,0 +1,27 @@
+router bgp 65002
+ timers bgp 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.7.7 remote-as external
+ neighbor 192.168.7.7 timers connect 5
+ neighbor 192.168.2.3 remote-as external
+ neighbor 192.168.2.3 timers connect 5
+ neighbor 192.168.2.3 weight 3
+ neighbor 192.168.2.4 remote-as external
+ neighbor 192.168.2.4 timers connect 5
+ neighbor 192.168.2.4 weight 4
+ neighbor 192.168.2.5 remote-as external
+ neighbor 192.168.2.5 timers connect 5
+ neighbor 192.168.2.5 weight 5
+ neighbor 192.168.2.6 remote-as external
+ neighbor 192.168.2.6 timers connect 5
+ neighbor 192.168.2.6 weight 6
+ address-family ipv4 unicast
+ neighbor 192.168.1.1 addpath-tx-best-selected 1
+ neighbor 192.168.1.1 prefix-list announce out
+ neighbor 192.168.7.7 addpath-tx-best-selected 2
+ neighbor 192.168.7.7 prefix-list announce out
+ exit-address-family
+!
+ip prefix-list announce seq 5 permit 172.16.16.254/32
+!
diff --git a/tests/topotests/bgp_addpath_best_selected/r2/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r2/zebra.conf
new file mode 100644
index 0000000..90587d2
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/r2/zebra.conf
@@ -0,0 +1,10 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+int r2-eth1
+ ip address 192.168.2.2/24
+!
+int r2-eth2
+ ip address 192.168.7.2/24
+!
diff --git a/tests/topotests/bgp_addpath_best_selected/r3/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r3/bgpd.conf
new file mode 100644
index 0000000..98eb2e1
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/r3/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65003
+ timers bgp 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_addpath_best_selected/r3/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r3/zebra.conf
new file mode 100644
index 0000000..417a484
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/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_addpath_best_selected/r4/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r4/bgpd.conf
new file mode 100644
index 0000000..68245c4
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/r4/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65004
+ timers bgp 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_addpath_best_selected/r4/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r4/zebra.conf
new file mode 100644
index 0000000..241e386
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/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_addpath_best_selected/r5/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r5/bgpd.conf
new file mode 100644
index 0000000..f36e2bd
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/r5/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65005
+ timers bgp 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_addpath_best_selected/r5/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r5/zebra.conf
new file mode 100644
index 0000000..203d229
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/r5/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 172.16.16.254/32
+!
+int r5-eth0
+ ip address 192.168.2.5/24
+!
diff --git a/tests/topotests/bgp_addpath_best_selected/r6/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r6/bgpd.conf
new file mode 100644
index 0000000..0d83ef8
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/r6/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65006
+ timers bgp 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_addpath_best_selected/r6/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r6/zebra.conf
new file mode 100644
index 0000000..894dd30
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/r6/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 172.16.16.254/32
+!
+int r6-eth0
+ ip address 192.168.2.6/24
+!
diff --git a/tests/topotests/bgp_addpath_best_selected/r7/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r7/bgpd.conf
new file mode 100644
index 0000000..090846a
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/r7/bgpd.conf
@@ -0,0 +1,7 @@
+!
+router bgp 65007
+ timers bgp 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.7.2 remote-as external
+ neighbor 192.168.7.2 timers connect 5
+!
diff --git a/tests/topotests/bgp_addpath_best_selected/r7/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r7/zebra.conf
new file mode 100644
index 0000000..55c70ba
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/r7/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r7-eth0
+ ip address 192.168.7.7/24
+!
diff --git a/tests/topotests/bgp_addpath_best_selected/test_bgp_addpath_best_selected.py b/tests/topotests/bgp_addpath_best_selected/test_bgp_addpath_best_selected.py
new file mode 100644
index 0000000..2a610c9
--- /dev/null
+++ b/tests/topotests/bgp_addpath_best_selected/test_bgp_addpath_best_selected.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if Add-Path best selected paths are announced per neighbor.
+"""
+
+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, 8):
+ 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"])
+ switch.add_link(tgen.gears["r5"])
+ switch.add_link(tgen.gears["r6"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r7"])
+ 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_addpath_best_selected():
+ 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 bgp ipv4 unicast 172.16.16.254/32 json"))
+ expected = {
+ "paths": [
+ {
+ "aspath": {
+ "string": "65006",
+ },
+ "weight": 6,
+ },
+ {
+ "aspath": {
+ "string": "65005",
+ },
+ "weight": 5,
+ },
+ {
+ "aspath": {
+ "string": "65004",
+ },
+ "weight": 4,
+ },
+ {
+ "aspath": {
+ "string": "65003",
+ },
+ "weight": 3,
+ },
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Can't converge initially"
+
+ def check_bgp_advertised_routes_to_r1():
+ output = json.loads(
+ r2.vtysh_cmd(
+ "show bgp ipv4 neighbors 192.168.1.1 advertised-routes detail json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "172.16.16.254/32": {
+ "paths": [
+ {
+ "aspath": {
+ "string": "65005",
+ }
+ },
+ {
+ "aspath": {
+ "string": "65006",
+ }
+ },
+ ]
+ }
+ },
+ "totalPrefixCounter": 2,
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(check_bgp_advertised_routes_to_r1)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert (
+ result is None
+ ), "Received more/less Add-Path best paths, but should be only 1+1 (real best path)"
+
+ def check_bgp_advertised_routes_to_r7():
+ output = json.loads(
+ r2.vtysh_cmd(
+ "show bgp ipv4 neighbors 192.168.7.7 advertised-routes detail json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "172.16.16.254/32": {
+ "paths": [
+ {
+ "aspath": {
+ "string": "65004",
+ }
+ },
+ {
+ "aspath": {
+ "string": "65005",
+ }
+ },
+ {
+ "aspath": {
+ "string": "65006",
+ }
+ },
+ ]
+ }
+ },
+ "totalPrefixCounter": 3,
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(check_bgp_advertised_routes_to_r7)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert (
+ result is None
+ ), "Received more/less Add-Path best paths, but should be only 2+1 (real best path)"
+
+
+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..5a4a5fb
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_matching_med/test_bgp_aggregate_address_matching_med.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+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..739685d
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_origin/test_bgp_aggregate-address_origin.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# bgp_aggregate-address_origin.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..cec0692
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# bgp_aggregate-address_route-map.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..370d01e
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py
@@ -0,0 +1,279 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_aggregate_address_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..d9ef3e1
--- /dev/null
+++ b/tests/topotests/bgp_aggregator_zero/test_bgp_aggregator_zero.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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_aigp/__init__.py b/tests/topotests/bgp_aigp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_aigp/__init__.py
diff --git a/tests/topotests/bgp_aigp/r1/bgpd.conf b/tests/topotests/bgp_aigp/r1/bgpd.conf
new file mode 100644
index 0000000..74a0215
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r1/bgpd.conf
@@ -0,0 +1,12 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.0.2 remote-as internal
+ neighbor 10.0.0.2 update-source lo
+ neighbor 10.0.0.2 timers 1 3
+ neighbor 10.0.0.2 timers connect 1
+ neighbor 10.0.0.3 remote-as internal
+ neighbor 10.0.0.3 timers 1 3
+ neighbor 10.0.0.3 timers connect 1
+ neighbor 10.0.0.3 update-source lo
+!
diff --git a/tests/topotests/bgp_aigp/r1/ospfd.conf b/tests/topotests/bgp_aigp/r1/ospfd.conf
new file mode 100644
index 0000000..38aa11d
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r1/ospfd.conf
@@ -0,0 +1,17 @@
+!
+interface lo
+ ip ospf cost 10
+!
+interface r1-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+ ip ospf cost 10
+!
+interface r1-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+ ip ospf cost 30
+!
+router ospf
+ router-id 10.0.0.1
+ network 0.0.0.0/0 area 0
diff --git a/tests/topotests/bgp_aigp/r1/zebra.conf b/tests/topotests/bgp_aigp/r1/zebra.conf
new file mode 100644
index 0000000..0ed22d3
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r1/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface lo
+ ip address 10.0.0.1/32
+!
+interface r1-eth0
+ ip address 192.168.12.1/24
+!
+interface r1-eth1
+ ip address 192.168.13.1/24
+!
diff --git a/tests/topotests/bgp_aigp/r2/bgpd.conf b/tests/topotests/bgp_aigp/r2/bgpd.conf
new file mode 100644
index 0000000..4db4687
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r2/bgpd.conf
@@ -0,0 +1,15 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.0.1 remote-as internal
+ neighbor 10.0.0.1 timers 1 3
+ neighbor 10.0.0.1 timers connect 1
+ neighbor 10.0.0.1 route-reflector-client
+ neighbor 192.168.24.4 remote-as internal
+ neighbor 192.168.24.4 timers 1 3
+ neighbor 192.168.24.4 timers connect 1
+ neighbor 192.168.24.4 aigp
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_aigp/r2/ospfd.conf b/tests/topotests/bgp_aigp/r2/ospfd.conf
new file mode 100644
index 0000000..ed31941
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r2/ospfd.conf
@@ -0,0 +1,13 @@
+!
+interface lo
+ ip ospf cost 10
+!
+interface r2-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+ ip ospf cost 10
+!
+router ospf
+ router-id 10.0.0.2
+ network 192.168.12.0/24 area 0
+ network 10.0.0.2/32 area 0
diff --git a/tests/topotests/bgp_aigp/r2/zebra.conf b/tests/topotests/bgp_aigp/r2/zebra.conf
new file mode 100644
index 0000000..6d6a557
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r2/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface lo
+ ip address 10.0.0.2/32
+!
+interface r2-eth0
+ ip address 192.168.12.2/24
+!
+interface r2-eth1
+ ip address 192.168.24.2/24
+!
diff --git a/tests/topotests/bgp_aigp/r3/bgpd.conf b/tests/topotests/bgp_aigp/r3/bgpd.conf
new file mode 100644
index 0000000..5ab712e
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r3/bgpd.conf
@@ -0,0 +1,15 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.0.1 remote-as internal
+ neighbor 10.0.0.1 timers 1 3
+ neighbor 10.0.0.1 timers connect 1
+ neighbor 10.0.0.1 route-reflector-client
+ neighbor 192.168.35.5 remote-as internal
+ neighbor 192.168.35.5 timers 1 3
+ neighbor 192.168.35.5 timers connect 1
+ neighbor 192.168.35.5 aigp
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_aigp/r3/ospfd.conf b/tests/topotests/bgp_aigp/r3/ospfd.conf
new file mode 100644
index 0000000..f971ad6
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r3/ospfd.conf
@@ -0,0 +1,13 @@
+!
+interface lo
+ ip ospf cost 10
+!
+interface r3-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+ ip ospf cost 30
+!
+router ospf
+ router-id 10.0.0.3
+ network 192.168.13.0/24 area 0
+ network 10.0.0.3/32 area 0
diff --git a/tests/topotests/bgp_aigp/r3/zebra.conf b/tests/topotests/bgp_aigp/r3/zebra.conf
new file mode 100644
index 0000000..82c87d5
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r3/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface lo
+ ip address 10.0.0.3/32
+!
+interface r3-eth0
+ ip address 192.168.13.3/24
+!
+interface r3-eth1
+ ip address 192.168.35.3/24
+!
diff --git a/tests/topotests/bgp_aigp/r4/bgpd.conf b/tests/topotests/bgp_aigp/r4/bgpd.conf
new file mode 100644
index 0000000..aa88bac
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r4/bgpd.conf
@@ -0,0 +1,17 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.24.2 remote-as internal
+ neighbor 192.168.24.2 timers 1 3
+ neighbor 192.168.24.2 timers connect 1
+ neighbor 192.168.24.2 aigp
+ neighbor 192.168.24.2 route-reflector-client
+ neighbor 10.0.0.6 remote-as internal
+ neighbor 10.0.0.6 timers 1 3
+ neighbor 10.0.0.6 timers connect 1
+ neighbor 10.0.0.6 update-source lo
+ address-family ipv4
+ redistribute connected
+ redistribute ospf
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_aigp/r4/ospfd.conf b/tests/topotests/bgp_aigp/r4/ospfd.conf
new file mode 100644
index 0000000..c9e6796
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r4/ospfd.conf
@@ -0,0 +1,13 @@
+!
+interface lo
+ ip ospf cost 10
+!
+interface r4-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+ ip ospf cost 20
+!
+router ospf
+ router-id 10.0.0.4
+ network 192.168.46.0/24 area 0
+ network 10.0.0.4/32 area 0
diff --git a/tests/topotests/bgp_aigp/r4/zebra.conf b/tests/topotests/bgp_aigp/r4/zebra.conf
new file mode 100644
index 0000000..5f544e8
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r4/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface lo
+ ip address 10.0.0.4/32
+!
+interface r4-eth0
+ ip address 192.168.24.4/24
+!
+interface r4-eth1
+ ip address 192.168.46.4/24
+!
diff --git a/tests/topotests/bgp_aigp/r5/bgpd.conf b/tests/topotests/bgp_aigp/r5/bgpd.conf
new file mode 100644
index 0000000..4fde262
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r5/bgpd.conf
@@ -0,0 +1,17 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.35.3 remote-as internal
+ neighbor 192.168.35.3 timers 1 3
+ neighbor 192.168.35.3 timers connect 1
+ neighbor 192.168.35.3 aigp
+ neighbor 192.168.35.3 route-reflector-client
+ neighbor 10.0.0.6 remote-as internal
+ neighbor 10.0.0.6 timers 1 3
+ neighbor 10.0.0.6 timers connect 1
+ neighbor 10.0.0.6 update-source lo
+ address-family ipv4
+ redistribute connected
+ redistribute ospf
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_aigp/r5/ospfd.conf b/tests/topotests/bgp_aigp/r5/ospfd.conf
new file mode 100644
index 0000000..b853c74
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r5/ospfd.conf
@@ -0,0 +1,13 @@
+!
+interface lo
+ ip ospf cost 10
+!
+interface r5-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+ ip ospf cost 10
+!
+router ospf
+ router-id 10.0.0.5
+ network 192.168.56.0/24 area 0
+ network 10.0.0.5/32 area 0
diff --git a/tests/topotests/bgp_aigp/r5/zebra.conf b/tests/topotests/bgp_aigp/r5/zebra.conf
new file mode 100644
index 0000000..69b8bf2
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r5/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface lo
+ ip address 10.0.0.5/32
+!
+interface r5-eth0
+ ip address 192.168.35.5/24
+!
+interface r5-eth1
+ ip address 192.168.56.5/24
+!
diff --git a/tests/topotests/bgp_aigp/r6/bgpd.conf b/tests/topotests/bgp_aigp/r6/bgpd.conf
new file mode 100644
index 0000000..2faae77
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r6/bgpd.conf
@@ -0,0 +1,20 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.0.4 remote-as internal
+ neighbor 10.0.0.4 timers 1 3
+ neighbor 10.0.0.4 timers connect 1
+ neighbor 10.0.0.4 route-reflector-client
+ neighbor 10.0.0.4 update-source lo
+ neighbor 10.0.0.5 remote-as internal
+ neighbor 10.0.0.5 timers 1 3
+ neighbor 10.0.0.5 timers connect 1
+ neighbor 10.0.0.5 route-reflector-client
+ neighbor 10.0.0.5 update-source lo
+ neighbor 192.168.67.7 remote-as internal
+ neighbor 192.168.67.7 timers 1 3
+ neighbor 192.168.67.7 timers connect 1
+ address-family ipv4
+ redistribute ospf
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_aigp/r6/ospfd.conf b/tests/topotests/bgp_aigp/r6/ospfd.conf
new file mode 100644
index 0000000..46b2933
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r6/ospfd.conf
@@ -0,0 +1,17 @@
+!
+interface lo
+ ip ospf cost 10
+!
+interface r6-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+ ip ospf cost 20
+!
+interface r6-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+ ip ospf cost 10
+!
+router ospf
+ router-id 10.0.0.6
+ network 0.0.0.0/0 area 0
diff --git a/tests/topotests/bgp_aigp/r6/zebra.conf b/tests/topotests/bgp_aigp/r6/zebra.conf
new file mode 100644
index 0000000..f8ca5f8
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r6/zebra.conf
@@ -0,0 +1,13 @@
+!
+interface lo
+ ip address 10.0.0.6/32
+!
+interface r6-eth0
+ ip address 192.168.46.6/24
+!
+interface r6-eth1
+ ip address 192.168.56.6/24
+!
+interface r6-eth2
+ ip address 192.168.67.6/24
+!
diff --git a/tests/topotests/bgp_aigp/r7/bgpd.conf b/tests/topotests/bgp_aigp/r7/bgpd.conf
new file mode 100644
index 0000000..639dcfe
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r7/bgpd.conf
@@ -0,0 +1,22 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.67.6 remote-as internal
+ neighbor 192.168.67.6 timers 1 3
+ neighbor 192.168.67.6 timers connect 1
+ address-family ipv4
+ redistribute connected route-map rmap metric 71
+ exit-address-family
+!
+ip prefix-list p71 seq 5 permit 10.0.0.71/32
+ip prefix-list p72 seq 5 permit 10.0.0.72/32
+!
+route-map rmap permit 10
+ match ip address prefix-list p71
+ set aigp igp-metric
+!
+route-map rmap permit 20
+ match ip address prefix-list p72
+ set aigp 72
+exit
+!
diff --git a/tests/topotests/bgp_aigp/r7/zebra.conf b/tests/topotests/bgp_aigp/r7/zebra.conf
new file mode 100644
index 0000000..4c05df8
--- /dev/null
+++ b/tests/topotests/bgp_aigp/r7/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 10.0.0.7/32
+ ip address 10.0.0.71/32
+ ip address 10.0.0.72/32
+!
+interface r7-eth0
+ ip address 192.168.67.7/24
+!
diff --git a/tests/topotests/bgp_aigp/test_bgp_aigp.py b/tests/topotests/bgp_aigp/test_bgp_aigp.py
new file mode 100644
index 0000000..655e9ad
--- /dev/null
+++ b/tests/topotests/bgp_aigp/test_bgp_aigp.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+r7 sets aigp-metric for 10.0.0.71/32 to 71, and 72 for 10.0.0.72/32.
+
+r6 receives those routes with aigp-metric TLV.
+
+r2 and r3 receives those routes with aigp-metric TLV increased by 20,
+and 30 appropriately.
+
+r1 receives routes with aigp-metric TLV 111,131 and 112,132 appropriately.
+"""
+
+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, 8):
+ 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["r5"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r4"])
+ switch.add_link(tgen.gears["r6"])
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r5"])
+ switch.add_link(tgen.gears["r6"])
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["r6"])
+ switch.add_link(tgen.gears["r7"])
+
+
+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_OSPF, os.path.join(CWD, "{}/ospfd.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_aigp():
+ 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"]
+
+ def _bgp_converge():
+ output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 10.0.0.71/32 json"))
+ expected = {
+ "paths": [
+ {
+ "aigpMetric": 111,
+ "valid": True,
+ "nexthops": [{"hostname": "r3", "accessible": True}],
+ },
+ {
+ "aigpMetric": 131,
+ "valid": True,
+ "bestpath": {"selectionReason": "Neighbor IP"},
+ "nexthops": [{"hostname": "r2", "accessible": True}],
+ },
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_aigp_metric(router, prefix, aigp):
+ output = json.loads(
+ router.vtysh_cmd("show bgp ipv4 unicast {} json".format(prefix))
+ )
+ expected = {"paths": [{"aigpMetric": aigp, "valid": True}]}
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_aigp_metric_bestpath():
+ output = json.loads(
+ r1.vtysh_cmd(
+ "show bgp ipv4 unicast 10.0.0.64/28 longer-prefixes json detail"
+ )
+ )
+ expected = {
+ "routes": {
+ "10.0.0.71/32": {
+ "paths": [
+ {
+ "aigpMetric": 111,
+ "bestpath": {"selectionReason": "AIGP"},
+ "valid": True,
+ "nexthops": [{"hostname": "r3", "accessible": True}],
+ },
+ {
+ "aigpMetric": 131,
+ "valid": True,
+ "nexthops": [{"hostname": "r2", "accessible": True}],
+ },
+ ],
+ },
+ "10.0.0.72/32": {
+ "paths": [
+ {
+ "aigpMetric": 112,
+ "bestpath": {"selectionReason": "AIGP"},
+ "valid": True,
+ "nexthops": [{"hostname": "r3", "accessible": True}],
+ },
+ {
+ "aigpMetric": 132,
+ "valid": True,
+ "nexthops": [{"hostname": "r2", "accessible": True}],
+ },
+ ],
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ # Initial converge, AIGP is not involved in best-path selection process
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "can't converge initially"
+
+ # Enable `bgp bestpath aigp`
+ r1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ bgp bestpath aigp
+ """
+ )
+
+ # r4, 10.0.0.71/32 with aigp-metric 71
+ test_func = functools.partial(_bgp_check_aigp_metric, r4, "10.0.0.71/32", 71)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "aigp-metric for 10.0.0.71/32 is not 71"
+
+ # r5, 10.0.0.72/32 with aigp-metric 72
+ test_func = functools.partial(_bgp_check_aigp_metric, r5, "10.0.0.72/32", 72)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "aigp-metric for 10.0.0.72/32 is not 72"
+
+ # r2, 10.0.0.71/32 with aigp-metric 101 (71 + 30)
+ test_func = functools.partial(_bgp_check_aigp_metric, r2, "10.0.0.71/32", 101)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "aigp-metric for 10.0.0.71/32 is not 101"
+
+ # r3, 10.0.0.72/32 with aigp-metric 92 (72 + 20)
+ test_func = functools.partial(_bgp_check_aigp_metric, r3, "10.0.0.72/32", 92)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "aigp-metric for 10.0.0.72/32 is not 92"
+
+ # r1, check if AIGP is considered in best-path selection (lowest wins)
+ test_func = functools.partial(_bgp_check_aigp_metric_bestpath)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "AIGP attribute is not considered in best-path selection"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_always_compare_med/bgp_always_compare_med_topo1.json b/tests/topotests/bgp_always_compare_med/bgp_always_compare_med_topo1.json
new file mode 100644
index 0000000..4156c6d
--- /dev/null
+++ b/tests/topotests/bgp_always_compare_med/bgp_always_compare_med_topo1.json
@@ -0,0 +1,152 @@
+{
+ "address_types": ["ipv4", "ipv6"],
+ "ipv4base": "192.168.0.0",
+ "ipv4mask": 30,
+ "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": {
+ "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"
+ }
+ }
+ }]
+ },
+ "static_routes":[
+ {
+ "network":"192.168.20.1/32",
+ "next_hop":"Null0"
+ },
+ {
+ "network":"192:168:20::1/128",
+ "next_hop":"Null0"
+ }]
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "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": {
+ "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_always_compare_med/test_bgp_always_compare_med_topo1.py b/tests/topotests/bgp_always_compare_med/test_bgp_always_compare_med_topo1.py
new file mode 100644
index 0000000..fb72f43
--- /dev/null
+++ b/tests/topotests/bgp_always_compare_med/test_bgp_always_compare_med_topo1.py
@@ -0,0 +1,1118 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2023 by VMware, Inc. ("VMware")
+#
+#
+################################################################################
+# Following tests are performed to validate BGP always compare MED functionality
+################################################################################
+"""
+1. Verify the BGP always compare MED functionality in between eBGP Peers
+2. Verify the BGP always compare MED functionality in between eBGP Peers with by changing different AD values
+3. Verify the BGP always compare MED functionality in between eBGP Peers by changing MED values in middle routers
+4. Verify that BGP Always compare MED functionality by restarting BGP, Zebra and FRR services and clear BGP and
+ shutdown BGP neighbor
+5. Verify BGP always compare MED functionality by performing shut/noshut on the interfaces in between BGP neighbors
+"""
+
+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 lib.common_config import (
+ start_topology,
+ write_test_header,
+ create_static_routes,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ check_address_types,
+ check_router_status,
+ create_static_routes,
+ create_prefix_lists,
+ create_route_maps,
+ kill_router_daemons,
+ shutdown_bringup_interface,
+ stop_router,
+ start_router,
+ delete_route_maps,
+)
+
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence, verify_bgp_rib, create_router_bgp, clear_bgp
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Reading the data from JSON File for topology creation
+topo = None
+
+# Global variables
+ADDR_TYPES = check_address_types()
+NETWORK1_1 = {"ipv4": "192.168.20.1/32", "ipv6": "192:168:20::1/128"}
+NETWORK1_2 = {"ipv4": "192.168.30.1/32", "ipv6": "192:168:30::1/128"}
+NETWORK1_3 = {"ipv4": "192.168.40.1/32", "ipv6": "192:168:40::1/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+
+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_always_compare_med_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
+ 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 API
+#
+##########################################################################################################
+
+
+def initial_configuration(tgen, tc_name):
+ """
+ API to do initial set of configuration
+ """
+
+ step(
+ "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config"
+ )
+
+ step("Configure static routes in R4")
+ for addr_type in ADDR_TYPES:
+ input_static_r4 = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_static_r4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure redistribute static in R4")
+ input_static_redist_r4 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ # Create prefix list
+ input_dict_23 = {
+ "r2": {
+ "prefix_lists": {
+ addr_type: {
+ "pf_ls_r2_{}".format(addr_type): [
+ {"network": NETWORK1_1[addr_type], "action": "permit"}
+ ]
+ }
+ }
+ },
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_ls_r3_{}".format(addr_type): [
+ {"network": NETWORK1_1[addr_type], "action": "permit"}
+ ]
+ }
+ }
+ },
+ }
+ result = create_prefix_lists(tgen, input_dict_23)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ input_dict_23 = {
+ "r2": {
+ "route_maps": {
+ "RMAP_MED_R2": [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_ls_r2_{}".format(addr_type)
+ }
+ },
+ "set": {"med": 300},
+ }
+ ]
+ }
+ },
+ "r3": {
+ "route_maps": {
+ "RMAP_MED_R3": [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_ls_r3_{}".format(addr_type)
+ }
+ },
+ "set": {"med": 200},
+ }
+ ]
+ }
+ },
+ }
+ result = create_route_maps(tgen, input_dict_23)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_r2_r3 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "RMAP_MED_R2",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "RMAP_MED_R3",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+
+##########################################################################################################
+#
+# Testcases
+#
+##########################################################################################################
+
+
+def test_verify_bgp_always_compare_med_functionality_bw_eBGP_peers_p0(request):
+ """
+ Verify the BGP always compare MED functionality in between eBGP Peers
+ """
+
+ # test case name
+ 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)
+ initial_configuration(tgen, tc_name)
+
+ step(
+ "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config"
+ )
+ step(
+ "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following "
+ "commands and verify that best path chosen by lowest MED value"
+ )
+
+ 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_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure 'multi-path as-path relax' command at R1.")
+ configure_bgp = {
+ "r1": {"bgp": {"local_as": "100", "bestpath": {"aspath": "multipath-relax"}}}
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that after applying 'multi-path as-path relax' command, "
+ "its also chooses lowest MED to reach destination."
+ )
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0]
+ nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2])
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure 'bgp always-compare-med' command at R1.")
+ input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}}
+ result = create_router_bgp(tgen, topo, input_dict_r1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path"
+ )
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove 'bgp always-compare-med' command at R1.")
+ input_dict_r1 = {
+ "r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": False}}
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that 'bgp always-compare-med' command is removed")
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0]
+ nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2])
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove 'multi-path as-path relax' command at R1")
+ configure_bgp = {
+ "r1": {
+ "bgp": {
+ "local_as": "100",
+ "bestpath": {"aspath": "multipath-relax", "delete": True},
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify route selection after removing 'multi-path as-path relax' command")
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_always_compare_med_functionality_bw_eBGP_peers_by_changing_AD_values_p0(
+ request,
+):
+ """
+ Verify the BGP always compare MED functionality in between eBGP Peers with by changing different AD values.
+ """
+
+ # test case name
+ 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)
+ initial_configuration(tgen, tc_name)
+
+ step(
+ "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config"
+ )
+ step(
+ "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following "
+ "commands and verify that best path chosen by lowest MED value"
+ )
+
+ 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_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure 'bgp always-compare-med' command at R1.")
+ input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}}
+ result = create_router_bgp(tgen, topo, input_dict_r1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path"
+ )
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure AD value=100 at R2 and AD value=200 at R3 towards R1")
+ input_dict_1 = {
+ "r2": {
+ "bgp": {
+ "local_as": 200,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 100, "ibgp": 100, "local": 100}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 100, "ibgp": 100, "local": 100}
+ }
+ },
+ },
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": 300,
+ "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 that inspite of AD values, always lowest MED value is getting "
+ "selected at destination router R1"
+ )
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_always_compare_med_functionality_bw_eBGP_peers_by_changing_MED_values_p1(
+ request,
+):
+ """
+ Verify the BGP always compare MED functionality in between eBGP Peers by changing MED values in middle routers
+ """
+
+ # test case name
+ 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)
+ initial_configuration(tgen, tc_name)
+
+ step(
+ "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config"
+ )
+ step(
+ "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following "
+ "commands and verify that best path chosen by lowest MED value"
+ )
+
+ 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_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure 'multi-path as-path relax' command at R1.")
+ configure_bgp = {
+ "r1": {"bgp": {"local_as": "100", "bestpath": {"aspath": "multipath-relax"}}}
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that after applying 'multi-path as-path relax' command, "
+ "its also chooses lowest MED to reach destination."
+ )
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0]
+ nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2])
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure 'bgp always-compare-med' command at R1.")
+ input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}}
+ result = create_router_bgp(tgen, topo, input_dict_r1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path"
+ )
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Change the MED value 150 in R2 router.")
+ input_dict = {"r2": {"route_maps": ["RMAP_MED_R2"]}}
+ result = delete_route_maps(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "route_maps": {
+ "RMAP_MED_R2": [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_ls_r2_{}".format(addr_type)
+ }
+ },
+ "set": {"med": 150},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that after changing MED, its chooses lowest MED value path")
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Change the MED value 100 in R3 router.")
+ input_dict = {"r3": {"route_maps": ["RMAP_MED_R3"]}}
+ result = delete_route_maps(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "RMAP_MED_R3": [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_ls_r3_{}".format(addr_type)
+ }
+ },
+ "set": {"med": 100},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that after changing MED, its chooses lowest MED value path")
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_always_compare_med_functionality_by_restarting_daemons_clear_bgp_shut_neighbors_p1(
+ request,
+):
+ """
+ Verify that BGP Always compare MED functionality by restarting BGP, Zebra and FRR services and clear BGP and shutdown BGP neighbor
+ """
+
+ # test case name
+ 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)
+ initial_configuration(tgen, tc_name)
+
+ step(
+ "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config"
+ )
+ step(
+ "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following "
+ "commands and verify that best path chosen by lowest MED value"
+ )
+
+ 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_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure 'multi-path as-path relax' command at R1.")
+ configure_bgp = {
+ "r1": {"bgp": {"local_as": "100", "bestpath": {"aspath": "multipath-relax"}}}
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that after applying 'multi-path as-path relax' command, "
+ "its also chooses lowest MED to reach destination."
+ )
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0]
+ nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2])
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure 'bgp always-compare-med' command at R1.")
+ input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}}
+ result = create_router_bgp(tgen, topo, input_dict_r1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path"
+ )
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Restart the BGPd/Zebra/FRR service on R1")
+ for daemon in ["bgpd", "zebra", "frr"]:
+ if daemon == "frr":
+ stop_router(tgen, "r1")
+ start_router(tgen, "r1")
+ else:
+ kill_router_daemons(tgen, "r1", daemon)
+
+ step(
+ "Verify after restarting dameons and frr services, its chooses lowest MED value path"
+ )
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Clear bgp on R1")
+ clear_bgp(tgen, None, "r1")
+
+ step("Verify after clearing BGP, its chooses lowest MED value path")
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Perform BGP neighborship shut/no shut")
+ for action, keyword in zip([True, False], ["shut", "noshut"]):
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {"r1": {"shutdown": action}}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify after {} BGP, its chooses lowest MED value path".format(keyword))
+ if action:
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ else:
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_always_compare_med_functionality_by_shut_noshut_interfaces_bw_bgp_neighbors_p1(
+ request,
+):
+ """
+ Verify BGP always compare MED functionality by performing shut/noshut on the interfaces in between BGP neighbors
+ """
+
+ # test case name
+ 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)
+ initial_configuration(tgen, tc_name)
+
+ step(
+ "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config"
+ )
+ step(
+ "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following "
+ "commands and verify that best path chosen by lowest MED value"
+ )
+
+ 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_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure 'multi-path as-path relax' command at R1.")
+ configure_bgp = {
+ "r1": {"bgp": {"local_as": "100", "bestpath": {"aspath": "multipath-relax"}}}
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that after applying 'multi-path as-path relax' command, "
+ "its also chooses lowest MED to reach destination."
+ )
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0]
+ nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2])
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure 'bgp always-compare-med' command at R1.")
+ input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}}
+ result = create_router_bgp(tgen, topo, input_dict_r1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path"
+ )
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for action, keyword in zip([False, True], ["Shut", "No Shut"]):
+ step(
+ "{} the interface on the link between R3 & R4 and R2 & R4 routers".format(
+ keyword
+ )
+ )
+ intf2_4 = topo["routers"]["r2"]["links"]["r4"]["interface"]
+ intf3_4 = topo["routers"]["r3"]["links"]["r4"]["interface"]
+ for dut, intf in zip(["r2", "r3"], [intf2_4, intf3_4]):
+ shutdown_bringup_interface(tgen, dut, intf, action)
+
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]}
+ }
+ nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ if action:
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ else:
+ result = verify_bgp_rib(
+ tgen, addr_type, "r1", input_static_r1, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Routes are still present in BGP table\n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, "r1", input_static_r1, next_hop=nh, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Routes are still present in FIB \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_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..c49a2e5
--- /dev/null
+++ b/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py
@@ -0,0 +1,957 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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]
+
+# 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 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_as_allow_in.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
+
+ # 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..7cb4f81
--- /dev/null
+++ b/tests/topotests/bgp_as_override/test_bgp_as_override.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+"""
+
+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..5c09a6b
--- /dev/null
+++ b/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_as_wide_bgp_identifier.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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_asdot_regex/__init__.py b/tests/topotests/bgp_asdot_regex/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_asdot_regex/__init__.py
diff --git a/tests/topotests/bgp_asdot_regex/r1/bgpd.conf b/tests/topotests/bgp_asdot_regex/r1/bgpd.conf
new file mode 100644
index 0000000..4dd95dd
--- /dev/null
+++ b/tests/topotests/bgp_asdot_regex/r1/bgpd.conf
@@ -0,0 +1,27 @@
+router bgp 1.1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.255.2 remote-as 1.2
+ address-family ipv4 unicast
+ network 172.31.1.0/24 route-map rmapout
+ network 172.31.2.0/24 route-map rmapout
+ neighbor 192.168.255.2 route-map rmapin in
+ neighbor 192.168.255.2 activate
+ exit-address-family
+exit
+bgp as-path access-list only1_4 permit _1.4_
+bgp as-path access-list only65540 permit _65540_
+access-list 172313 permit 172.31.3.0/24
+access-list 172314 permit 172.31.4.0/24
+route-map rmapout permit 1
+ set as-path prepend 1.4
+exit
+route-map rmapin permit 1
+ match ip address 172313
+ match as-path only1_4
+exit
+route-map rmapin permit 2
+ match ip address 172314
+ match as-path only65540
+exit
+
diff --git a/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json b/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json
new file mode 100644
index 0000000..e3703bf
--- /dev/null
+++ b/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json
@@ -0,0 +1,80 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 3,
+ "routerId": "192.168.255.1",
+ "defaultLocPrf": 100,
+ "localAS": "1.1",
+ "routes": { "172.31.1.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"172.31.1.0",
+ "prefixLen":24,
+ "network":"172.31.1.0/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"1.4",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+]
+,"172.31.2.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"172.31.2.0",
+ "prefixLen":24,
+ "network":"172.31.2.0/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"1.4",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+]
+,"172.31.3.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"172.31.3.0",
+ "prefixLen":24,
+ "network":"172.31.3.0/24",
+ "metric":0,
+ "weight":0,
+ "peerId":"192.168.255.2",
+ "path":"1.2 1.4",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.255.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+]
+ } }
diff --git a/tests/topotests/bgp_asdot_regex/r1/zebra.conf b/tests/topotests/bgp_asdot_regex/r1/zebra.conf
new file mode 100644
index 0000000..6e9b0b4
--- /dev/null
+++ b/tests/topotests/bgp_asdot_regex/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_asdot_regex/r2/bgpd.conf b/tests/topotests/bgp_asdot_regex/r2/bgpd.conf
new file mode 100644
index 0000000..216dbd1
--- /dev/null
+++ b/tests/topotests/bgp_asdot_regex/r2/bgpd.conf
@@ -0,0 +1,26 @@
+router bgp 65538
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.255.1 remote-as 65537
+ address-family ipv4 unicast
+ network 172.31.3.0/24 route-map rmapout
+ network 172.31.4.0/24 route-map rmapout
+ neighbor 192.168.255.1 route-map rmapin in
+ neighbor 192.168.255.1 activate
+ exit-address-family
+exit
+bgp as-path access-list only65540 permit _65540_
+bgp as-path access-list only1_4 permit _1.4_
+access-list 172311 permit 172.31.1.0/24
+access-list 172312 permit 172.31.2.0/24
+route-map rmapout permit 1
+ set as-path prepend 65540
+exit
+route-map rmapin permit 1
+ match ip address 172311
+ match as-path only65540
+exit
+route-map rmapin permit 2
+ match ip address 172312
+ match as-path only1_4
+exit
diff --git a/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json b/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json
new file mode 100644
index 0000000..1af4ff7
--- /dev/null
+++ b/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json
@@ -0,0 +1,80 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 3,
+ "routerId": "192.168.255.2",
+ "defaultLocPrf": 100,
+ "localAS": 65538,
+ "routes": { "172.31.1.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"172.31.1.0",
+ "prefixLen":24,
+ "network":"172.31.1.0/24",
+ "metric":0,
+ "weight":0,
+ "peerId":"192.168.255.1",
+ "path":"65537 65540",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.255.1",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+]
+,"172.31.3.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"172.31.3.0",
+ "prefixLen":24,
+ "network":"172.31.3.0/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"65540",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+]
+,"172.31.4.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"172.31.4.0",
+ "prefixLen":24,
+ "network":"172.31.4.0/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"65540",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+]
+ } }
diff --git a/tests/topotests/bgp_asdot_regex/r2/zebra.conf b/tests/topotests/bgp_asdot_regex/r2/zebra.conf
new file mode 100644
index 0000000..6c14de5
--- /dev/null
+++ b/tests/topotests/bgp_asdot_regex/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_asdot_regex/test_bgp_asdot_regex.py b/tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py
new file mode 100644
index 0000000..5d5f165
--- /dev/null
+++ b/tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_asdot_regex.py
+# Part of Topotests
+#
+# 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_asdot_regex.py:
+
+Test how regex applies when asnotation to forge bgp config is based on dot or not.
+"""
+
+import os
+import sys
+import json
+import pytest
+from functools import partial
+
+# 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_asdot_regex():
+ 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": 1}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ logger.info("Check if neighbor sessions are up in {}".format(router1.name))
+ test_func = 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("waiting for bgp peers exchanging UPDATES")
+
+ for router in tgen.routers().values():
+ ref_file = "{}/{}/show_bgp_ipv4.json".format(CWD, router.name)
+ expected = json.loads(open(ref_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bgp ipv4 unicast json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=40, wait=2.5)
+ assertmsg = "{}: BGP UPDATE exchange failure".format(router.name)
+ assert res is None, assertmsg
+
+
+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..0f1a083
--- /dev/null
+++ b/tests/topotests/bgp_aspath_zero/test_bgp_aspath_zero.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..824498e
--- /dev/null
+++ b/tests/topotests/bgp_auth/bgp_auth_common.py
@@ -0,0 +1,266 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+"""
+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..9d47106
--- /dev/null
+++ b/tests/topotests/bgp_auth/test_bgp_auth1.py
@@ -0,0 +1,232 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+"""
+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..6b92036
--- /dev/null
+++ b/tests/topotests/bgp_auth/test_bgp_auth2.py
@@ -0,0 +1,240 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+"""
+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..2237c6b
--- /dev/null
+++ b/tests/topotests/bgp_auth/test_bgp_auth3.py
@@ -0,0 +1,221 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+"""
+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..d6fe425
--- /dev/null
+++ b/tests/topotests/bgp_auth/test_bgp_auth4.py
@@ -0,0 +1,238 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+"""
+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..c97fc5f
--- /dev/null
+++ b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py
@@ -0,0 +1,1169 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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, kernel version should be >=4.15")
+
+ 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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..e855f75
--- /dev/null
+++ b/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf
@@ -0,0 +1,11 @@
+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 timers connect 1
+ neighbor 192.168.255.2 bfd
+ neighbor 192.168.255.2 passive
+ 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..faf2c6b
--- /dev/null
+++ b/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf
@@ -0,0 +1,10 @@
+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 timers connect 1
+ 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..0014298
--- /dev/null
+++ b/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# bgp_bfd_down_cease_notification.py
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+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",
+ "lastNotificationHardReset": 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=1)
+ 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=1)
+ 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..eca12bd
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r4/bgpd.conf
@@ -0,0 +1,13 @@
+!
+router bgp 65002
+ timers bgp 3 9
+ no bgp ebgp-requires-policy
+ neighbor r4-eth0 interface remote-as internal
+!
+address-family ipv4 unicast
+ neighbor r4-eth0 route-map FOO in
+exit-address-family
+!
+route-map FOO permit 10
+ set ipv6 next-hop local fe80::202:ff:fe00:99
+exit
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..9f5c0ef
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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)
+
+ def _bgp_verify_nexthop_validity():
+ output = json.loads(tgen.gears["r4"].vtysh_cmd("show bgp nexthop json"))
+
+ expected = {
+ "ipv6": {
+ "fe80::202:ff:fe00:99": {
+ "valid": True,
+ "complete": True,
+ "igpMetric": 0,
+ "pathCount": 2,
+ "nexthops": [{"interfaceName": "r4-eth0"}],
+ },
+ }
+ }
+
+ 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"]
+ )
+
+ step("Verify if the nexthop set via route-map on r4 is marked valid")
+ test_func = functools.partial(_bgp_verify_nexthop_validity)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, 'Nexthops are not valid "{}"'.format(tgen.gears["r4"])
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_bmp/__init__.py b/tests/topotests/bgp_bmp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_bmp/__init__.py
diff --git a/tests/topotests/bgp_bmp/r1/bgpd.conf b/tests/topotests/bgp_bmp/r1/bgpd.conf
new file mode 100644
index 0000000..69acf6e
--- /dev/null
+++ b/tests/topotests/bgp_bmp/r1/bgpd.conf
@@ -0,0 +1,22 @@
+router bgp 65501
+ bgp router-id 192.168.0.1
+ bgp log-neighbor-changes
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.2 remote-as 65502
+ neighbor 192:168::2 remote-as 65502
+!
+ bmp targets bmp1
+ bmp connect 192.0.178.10 port 1789 min-retry 100 max-retry 10000
+ exit
+!
+ address-family ipv4 unicast
+ neighbor 192.168.0.2 activate
+ neighbor 192.168.0.2 soft-reconfiguration inbound
+ no neighbor 192:168::2 activate
+ exit-address-family
+!
+ address-family ipv6 unicast
+ neighbor 192:168::2 activate
+ neighbor 192:168::2 soft-reconfiguration inbound
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_bmp/r1/zebra.conf b/tests/topotests/bgp_bmp/r1/zebra.conf
new file mode 100644
index 0000000..6a25a6f
--- /dev/null
+++ b/tests/topotests/bgp_bmp/r1/zebra.conf
@@ -0,0 +1,7 @@
+interface r1-eth0
+ ip address 192.0.178.1/24
+!
+interface r1-eth1
+ ip address 192.168.0.1/24
+ ipv6 address 192:168::1/64
+!
diff --git a/tests/topotests/bgp_bmp/r2/bgpd.conf b/tests/topotests/bgp_bmp/r2/bgpd.conf
new file mode 100644
index 0000000..7c8255a
--- /dev/null
+++ b/tests/topotests/bgp_bmp/r2/bgpd.conf
@@ -0,0 +1,19 @@
+router bgp 65502
+ bgp router-id 192.168.0.2
+ bgp log-neighbor-changes
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.0.1 remote-as 65501
+ neighbor 192:168::1 remote-as 65501
+!
+ address-family ipv4 unicast
+ neighbor 192.168.0.1 activate
+ no neighbor 192:168::1 activate
+ redistribute connected
+ exit-address-family
+!
+ address-family ipv6 unicast
+ neighbor 192:168::1 activate
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_bmp/r2/zebra.conf b/tests/topotests/bgp_bmp/r2/zebra.conf
new file mode 100644
index 0000000..9d82bfe
--- /dev/null
+++ b/tests/topotests/bgp_bmp/r2/zebra.conf
@@ -0,0 +1,8 @@
+interface r2-eth0
+ ip address 192.168.0.2/24
+ ipv6 address 192:168::2/64
+!
+interface r2-eth1
+ ip address 172.31.0.2/24
+ ipv6 address 172:31::2/64
+!
diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp.py b/tests/topotests/bgp_bmp/test_bgp_bmp.py
new file mode 100644
index 0000000..65f191b
--- /dev/null
+++ b/tests/topotests/bgp_bmp/test_bgp_bmp.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright 2023 6WIND S.A.
+# Authored by Farid Mihoub <farid.mihoub@6wind.com>
+#
+
+"""
+test_bgp_bmp.py: Test BGP BMP functionalities
+
+ +------+ +------+ +------+
+ | | | | | |
+ | BMP1 |------------| R1 |---------------| R2 |
+ | | | | | |
+ +------+ +------+ +------+
+
+Setup two routers R1 and R2 with one link configured with IPv4 and
+IPv6 addresses.
+Configure BGP in R1 and R2 to exchange prefixes from
+the latter to the first router.
+Setup a link between R1 and the BMP server, activate the BMP feature in R1
+and ensure the monitored BGP sessions logs are well present on the BMP server.
+"""
+
+from functools import partial
+from ipaddress import ip_network
+import json
+import os
+import platform
+import pytest
+import sys
+
+# 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 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]
+
+# remember the last sequence number of the logging messages
+SEQ = 0
+
+PRE_POLICY = "pre-policy"
+POST_POLICY = "post-policy"
+
+
+def build_topo(tgen):
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+ tgen.add_bmp_server("bmp1", ip="192.0.178.10", defaultRoute="via 192.0.178.1")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["bmp1"])
+
+ tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "r1-eth1", "r2-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)),
+ "-M bmp",
+ )
+
+ tgen.start_router()
+
+ logger.info("starting BMP servers")
+ for _, server in tgen.get_bmp_servers().items():
+ server.start()
+
+
+def teardown_module(_mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_convergence():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ result = verify_bgp_convergence_from_running_config(tgen, dut="r1")
+ assert result is True, "BGP is not converging"
+
+
+def get_bmp_messages():
+ """
+ Read the BMP logging messages.
+ """
+ messages = []
+ tgen = get_topogen()
+ text_output = tgen.gears["bmp1"].run("cat /var/log/bmp.log")
+
+ for m in text_output.splitlines():
+ # some output in the bash can break the message decoding
+ try:
+ messages.append(json.loads(m))
+ except Exception as e:
+ logger.warning(str(e) + " message: {}".format(str(m)))
+ continue
+
+ if not messages:
+ logger.error("Bad BMP log format, check your BMP server")
+
+ return messages
+
+
+def check_for_prefixes(expected_prefixes, bmp_log_type, post_policy):
+ """
+ Check for the presence of the given prefixes in the BMP server logs with
+ the given message type and the set policy.
+ """
+ global SEQ
+ # we care only about the new messages
+ messages = [
+ m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ
+ ]
+
+ # get the list of pairs (prefix, policy, seq) for the given message type
+ prefixes = [
+ m["ip_prefix"]
+ for m in messages
+ if "ip_prefix" in m.keys()
+ and "bmp_log_type" in m.keys()
+ and m["bmp_log_type"] == bmp_log_type
+ and m["post_policy"] == post_policy
+ ]
+
+ # check for prefixes
+ for ep in expected_prefixes:
+ if ep not in prefixes:
+ msg = "The prefix {} is not present in the {} log messages."
+ logger.debug(msg.format(ep, bmp_log_type))
+ return False
+
+ SEQ = messages[-1]["seq"]
+ return True
+
+
+def set_bmp_policy(tgen, node, asn, target, safi, policy, vrf=None):
+ """
+ Configure the bmp policy.
+ """
+ vrf = " vrf {}" if vrf else ""
+ cmd = [
+ "con t\n",
+ "router bgp {}{}\n".format(asn, vrf),
+ "bmp targets {}\n".format(target),
+ "bmp monitor ipv4 {} {}\n".format(safi, policy),
+ "bmp monitor ipv6 {} {}\n".format(safi, policy),
+ "end\n",
+ ]
+ tgen.gears[node].vtysh_cmd("".join(cmd))
+
+
+def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True):
+ """
+ Configure the bgp prefixes.
+ """
+ withdraw = "no " if not update else ""
+ vrf = " vrf {}" if vrf else ""
+ for p in prefixes:
+ ip = ip_network(p)
+ cmd = [
+ "conf t\n",
+ "router bgp {}{}\n".format(asn, vrf),
+ "address-family ipv{} {}\n".format(ip.version, safi),
+ "{}network {}\n".format(withdraw, ip),
+ "exit-address-family\n",
+ ]
+ logger.debug("setting prefix: ipv{} {} {}".format(ip.version, safi, ip))
+ tgen.gears[node].vtysh_cmd("".join(cmd))
+
+
+def unicast_prefixes(policy):
+ """
+ Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes.
+ Check if the previous actions are logged in the BMP server with the right
+ message type and the right policy.
+ """
+ tgen = get_topogen()
+ set_bmp_policy(tgen, "r1", 65501, "bmp1", "unicast", policy)
+
+ prefixes = ["172.31.0.15/32", "2111::1111/128"]
+ # add prefixes
+ configure_prefixes(tgen, "r2", 65502, "unicast", prefixes)
+
+ logger.info("checking for updated prefixes")
+ # check
+ test_func = partial(check_for_prefixes, prefixes, "update", policy == POST_POLICY)
+ success, _ = topotest.run_and_expect(test_func, True, wait=0.5)
+ assert success, "Checking the updated prefixes has been failed !."
+
+ # withdraw prefixes
+ configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, update=False)
+ logger.info("checking for withdrawed prefxies")
+ # check
+ test_func = partial(check_for_prefixes, prefixes, "withdraw", policy == POST_POLICY)
+ success, _ = topotest.run_and_expect(test_func, True, wait=0.5)
+ assert success, "Checking the withdrawed prefixes has been failed !."
+
+
+def test_bmp_server_logging():
+ """
+ Assert the logging of the bmp server.
+ """
+
+ def check_for_log_file():
+ tgen = get_topogen()
+ output = tgen.gears["bmp1"].run("ls /var/log/")
+ if "bmp.log" not in output:
+ return False
+ return True
+
+ success, _ = topotest.run_and_expect(check_for_log_file, True, wait=0.5)
+ assert success, "The BMP server is not logging"
+
+
+def test_bmp_bgp_unicast():
+ """
+ Add/withdraw bgp unicast prefixes and check the bmp logs.
+ """
+ logger.info("*** Unicast prefixes pre-policy logging ***")
+ unicast_prefixes(PRE_POLICY)
+ logger.info("*** Unicast prefixes post-policy logging ***")
+ unicast_prefixes(POST_POLICY)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_color_extcommunities/__init__.py b/tests/topotests/bgp_color_extcommunities/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_color_extcommunities/__init__.py
diff --git a/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf b/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf
new file mode 100644
index 0000000..d4ca392
--- /dev/null
+++ b/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf
@@ -0,0 +1,17 @@
+!
+router bgp 65001
+ bgp router-id 192.168.1.1
+ 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.10/24 route-map rmap
+ neighbor 192.168.1.2 route-map rmap out
+ neighbor 192.168.1.2 activate
+ exit-address-family
+!
+route-map rmap permit 10
+ set extcommunity color 1
+ set extcommunity rt 80:987
+ set extcommunity color 100 55555 200
+exit
diff --git a/tests/topotests/bgp_color_extcommunities/r1/zebra.conf b/tests/topotests/bgp_color_extcommunities/r1/zebra.conf
new file mode 100644
index 0000000..42a8303
--- /dev/null
+++ b/tests/topotests/bgp_color_extcommunities/r1/zebra.conf
@@ -0,0 +1,3 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
diff --git a/tests/topotests/bgp_color_extcommunities/r2/bgpd.conf b/tests/topotests/bgp_color_extcommunities/r2/bgpd.conf
new file mode 100644
index 0000000..2f83ada
--- /dev/null
+++ b/tests/topotests/bgp_color_extcommunities/r2/bgpd.conf
@@ -0,0 +1,4 @@
+router bgp 65002
+ bgp router-id 192.168.1.2
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
diff --git a/tests/topotests/bgp_color_extcommunities/r2/zebra.conf b/tests/topotests/bgp_color_extcommunities/r2/zebra.conf
new file mode 100644
index 0000000..cffe827
--- /dev/null
+++ b/tests/topotests/bgp_color_extcommunities/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py b/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py
new file mode 100644
index 0000000..6d17cdb
--- /dev/null
+++ b/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright 2022 6WIND S.A.
+# Copyright 2023 6WIND S.A.
+# François Dumontet <francois.dumontet@6wind.com>
+#
+
+
+"""
+test_bgp_color_extcommunity.py: Test the FRR BGP color extented
+community feature
+"""
+
+import os
+import sys
+import json
+import functools
+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]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ 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):
+ "Sets up the pytest environment"
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+ logger.info("setup_module")
+
+ 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_bgp_color_extended_communities():
+ 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(r1.vtysh_cmd("show bgp summary json"))
+ expected = {
+ "ipv4Unicast": {
+ "peers": {
+ "192.168.1.2": {
+ "pfxSnt": 1,
+ "state": "Established",
+ },
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Failed announcing 10.10.10.10/32 to r2"
+
+ def _bgp_check_route(router, exists):
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 10.10.10.10 json"))
+ if exists:
+ expected = {
+ "prefix": "10.10.10.0/24",
+ "paths": [
+ {
+ "valid": True,
+ "extendedCommunity": {
+ "string": "RT:80:987 Color:100 Color:200 Color:55555"
+ },
+ }
+ ],
+ }
+ else:
+ expected = {}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_route, r2, True)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "10.10.10.0/24 ext community is correctly not installed, but SHOULD be"
+
+
+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..efc8f20
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_delete/test_bgp_comm-list_delete.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# bgp_comm-list_delete.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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
+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)
+
+ 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)
+
+ 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 initially"
+
+ def _bgp_comm_list_delete():
+ output = json.loads(r2.vtysh_cmd("show ip bgp 172.16.255.254/32 json"))
+ expected = {"paths": [{"community": {"list": ["333:333"]}}]}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_comm_list_delete)
+ _, result = topotest.run_and_expect(test_func, not None, count=60, wait=0.5)
+ assert result is not None, "333:333 community SHOULD be stripped from r1"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_comm_list_match/__init__.py b/tests/topotests/bgp_comm_list_match/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_match/__init__.py
diff --git a/tests/topotests/bgp_comm_list_match/r1/bgpd.conf b/tests/topotests/bgp_comm_list_match/r1/bgpd.conf
new file mode 100644
index 0000000..bac8412
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_match/r1/bgpd.conf
@@ -0,0 +1,28 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.2 remote-as external
+ neighbor 192.168.0.2 timers 1 3
+ neighbor 192.168.0.2 timers connect 1
+ address-family ipv4
+ redistribute connected
+ neighbor 192.168.0.2 route-map r2 out
+ exit-address-family
+!
+ip prefix-list p1 seq 5 permit 172.16.255.1/32
+ip prefix-list p3 seq 5 permit 172.16.255.3/32
+ip prefix-list p4 seq 5 permit 172.16.255.4/32
+!
+route-map r2 permit 10
+ match ip address prefix-list p1
+ set community 65001:1 65001:2
+route-map r2 permit 20
+ match ip address prefix-list p3
+ set community 65001:3
+route-map r2 permit 30
+ match ip address prefix-list p4
+ set community 65001:10 65001:12 65001:13
+exit
+route-map r2 permit 40
+exit
+!
diff --git a/tests/topotests/bgp_comm_list_match/r1/zebra.conf b/tests/topotests/bgp_comm_list_match/r1/zebra.conf
new file mode 100644
index 0000000..4219a7c
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_match/r1/zebra.conf
@@ -0,0 +1,12 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+ ip address 172.16.255.2/32
+ ip address 172.16.255.3/32
+ ip address 172.16.255.4/32
+!
+interface r1-eth0
+ ip address 192.168.0.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_comm_list_match/r2/bgpd.conf b/tests/topotests/bgp_comm_list_match/r2/bgpd.conf
new file mode 100644
index 0000000..cb2f89e
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_match/r2/bgpd.conf
@@ -0,0 +1,24 @@
+!
+!debug bgp updates
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.1 remote-as external
+ neighbor 192.168.0.1 timers 1 3
+ neighbor 192.168.0.1 timers connect 1
+ neighbor 192.168.1.3 remote-as external
+ neighbor 192.168.1.3 timers 1 3
+ neighbor 192.168.1.3 timers connect 1
+ address-family ipv4
+ neighbor 192.168.0.1 route-map r1 in
+ neighbor 192.168.0.1 soft-reconfiguration inbound
+ exit-address-family
+!
+bgp community-list 1 seq 5 permit 65001:1 65001:2
+bgp community-list 1 seq 10 permit 65001:3
+!
+route-map r1 deny 10
+ match community 1
+route-map r1 permit 20
+exit
+!
diff --git a/tests/topotests/bgp_comm_list_match/r2/zebra.conf b/tests/topotests/bgp_comm_list_match/r2/zebra.conf
new file mode 100644
index 0000000..7fe82ba
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_match/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
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_comm_list_match/r3/bgpd.conf b/tests/topotests/bgp_comm_list_match/r3/bgpd.conf
new file mode 100644
index 0000000..e68a3e4
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_match/r3/bgpd.conf
@@ -0,0 +1,21 @@
+!
+!debug bgp updates
+!
+router bgp 65003
+ 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
+ address-family ipv4
+ neighbor 192.168.1.2 route-map r1 in
+ neighbor 192.168.1.2 soft-reconfiguration inbound
+ exit-address-family
+!
+bgp community-list 2 seq 10 permit 65001:12
+!
+route-map r1 deny 10
+ match community 2 any
+exit
+route-map r1 permit 20
+exit
+!
diff --git a/tests/topotests/bgp_comm_list_match/r3/zebra.conf b/tests/topotests/bgp_comm_list_match/r3/zebra.conf
new file mode 100644
index 0000000..755dd18
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_match/r3/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r3-eth0
+ ip address 192.168.1.3/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py b/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py
new file mode 100644
index 0000000..de69ea9
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Check if BGP community-list works as OR if multiple community entries specified,
+like:
+
+bgp community-list 1 seq 5 permit 65001:1 65002:2
+bgp community-list 1 seq 10 permit 65001:3
+!
+route-map test deny 10
+ match community 1
+route-map test permit 20
+
+Here, we should deny routes in/out if the path has:
+(65001:1 AND 65001:2) OR 65001:3.
+"""
+
+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, 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["r3"])
+ 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_comm_list_match():
+ 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 bgp ipv4 unicast neighbors 192.168.0.1 filtered-routes json"
+ )
+ )
+ expected = {
+ "receivedRoutes": {
+ "172.16.255.1/32": {
+ "path": "65001",
+ },
+ "172.16.255.3/32": {
+ "path": "65001",
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Initial BGP converge between R1 and R2")
+ 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 filter BGP UPDATES with community-list on R2"
+
+
+def test_bgp_comm_list_match_any():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r3"]
+
+ def _bgp_converge():
+ output = json.loads(
+ router.vtysh_cmd(
+ "show bgp ipv4 unicast neighbors 192.168.1.2 filtered-routes json"
+ )
+ )
+ expected = {
+ "receivedRoutes": {
+ "172.16.255.4/32": {
+ "path": "65002 65001",
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Initial BGP converge between R3 and R2")
+ 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 filter BGP UPDATES with community-list on R3"
+
+
+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..f3e03a0
--- /dev/null
+++ b/tests/topotests/bgp_communities_topo1/test_bgp_communities.py
@@ -0,0 +1,631 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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, kernel version should be >= 4.15")
+
+ 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 Expected: "
+ "Routes still present in {} router. Found: {}".format(tc_name, dut, result)
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Expected: Routes still present in {} router. Found: {}".format(
+ tc_name, dut, 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..324f53f
--- /dev/null
+++ b/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py
@@ -0,0 +1,371 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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
+
+"""
+
+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, kernel version should be >= 4.14")
+
+ 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_communities_p0(request):
+ """
+ Verify that BGP well known communities work fine for
+ eBGP and iBGP peers.
+ Well known communities tested: no-export, local-AS
+ """
+
+ 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"]:
+
+ 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
+ )
+
+ 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 "
+ "Expected: Routes are still present in rib of r3 \n "
+ "Found: {}".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..000ea60
--- /dev/null
+++ b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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": {
+ "paths": [
+ {
+ "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..5ad15e0
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py
@@ -0,0 +1,209 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+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..0128c88
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py
@@ -0,0 +1,1297 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_conditional_advertisement.py
+#
+# Copyright (c) 2020 by
+# Samsung R&D Institute India - Bangalore.
+# Madhurilatha Kuruganti
+#
+
+"""
+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_static_route/__init__.py b/tests/topotests/bgp_conditional_advertisement_static_route/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_static_route/__init__.py
diff --git a/tests/topotests/bgp_conditional_advertisement_static_route/r1/frr.conf b/tests/topotests/bgp_conditional_advertisement_static_route/r1/frr.conf
new file mode 100644
index 0000000..3e51337
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_static_route/r1/frr.conf
@@ -0,0 +1,10 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as internal
+ neighbor 192.168.1.2 timers 1 3
+ neighbor 192.168.1.2 timers connect 1
+!
diff --git a/tests/topotests/bgp_conditional_advertisement_static_route/r2/frr.conf b/tests/topotests/bgp_conditional_advertisement_static_route/r2/frr.conf
new file mode 100644
index 0000000..3ced934
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_static_route/r2/frr.conf
@@ -0,0 +1,40 @@
+!
+!debug bgp conditional-advertisement
+!debug bgp updates
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+int r2-eth1
+ ip address 192.168.2.2/24
+!
+router bgp 65000
+ no bgp ebgp-requires-policy
+ bgp conditional-advertisement timer 5
+ neighbor 192.168.1.1 remote-as internal
+ neighbor 192.168.1.1 timers 1 3
+ neighbor 192.168.1.1 timers connect 1
+ neighbor 192.168.2.1 remote-as internal
+ neighbor 192.168.2.1 timers 1 3
+ neighbor 192.168.2.1 timers connect 1
+ address-family ipv4 unicast
+ redistribute static
+ neighbor 192.168.1.1 advertise-map advertise-map exist-map exist-map
+ neighbor 192.168.1.1 route-map deny-all out
+ exit-address-family
+!
+ip route 10.10.10.1/32 r2-eth0
+ip route 10.10.10.2/32 r2-eth0
+!
+ip prefix-list default seq 5 permit 0.0.0.0/0
+ip prefix-list advertise seq 5 permit 10.10.10.1/32
+!
+route-map deny-all deny 10
+!
+route-map exist-map permit 10
+ match ip address prefix-list default
+!
+route-map advertise-map permit 10
+ match ip address prefix-list advertise
+ set community 65000:1
+!
diff --git a/tests/topotests/bgp_conditional_advertisement_static_route/r3/frr.conf b/tests/topotests/bgp_conditional_advertisement_static_route/r3/frr.conf
new file mode 100644
index 0000000..a24a2cb
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_static_route/r3/frr.conf
@@ -0,0 +1,19 @@
+!
+int lo
+ ip address 10.10.10.1/32
+ ip address 10.10.10.2/32
+!
+int r3-eth0
+ ip address 192.168.2.1/24
+!
+router bgp 65000
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.2.2 remote-as internal
+ neighbor 192.168.2.2 timers 1 3
+ neighbor 192.168.2.2 timers connect 1
+ !
+ address-family ipv4 unicast
+ neighbor 192.168.2.2 default-originate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_conditional_advertisement_static_route/test_bgp_conditional_advertisement_static_route.py b/tests/topotests/bgp_conditional_advertisement_static_route/test_bgp_conditional_advertisement_static_route.py
new file mode 100644
index 0000000..4180bfc
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_static_route/test_bgp_conditional_advertisement_static_route.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if static route with BGP conditional advertisement works correctly
+if we modify the prefix-lists.
+"""
+
+import os
+import re
+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 step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2"), "s2": ("r2", "r3")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for _, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_conditional_advertisements_static_route():
+ 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 unicast neighbor 192.168.1.1 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "10.10.10.1/32": {
+ "valid": True,
+ }
+ },
+ "totalPrefixCounter": 1,
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_converge,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Can't converge"
+
+ step("Append prefix-list to advertise 10.10.10.2/32")
+
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ ip prefix-list advertise seq 10 permit 10.10.10.2/32
+ """
+ )
+
+ def _bgp_check_advertised_after_update():
+ output = json.loads(
+ r2.vtysh_cmd(
+ "show bgp ipv4 unicast neighbor 192.168.1.1 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "10.10.10.1/32": {
+ "valid": True,
+ },
+ "10.10.10.2/32": {
+ "valid": True,
+ },
+ },
+ "totalPrefixCounter": 2,
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_check_advertised_after_update,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "10.10.10.2/32 is not advertised after prefix-list update"
+
+ def _bgp_check_received_routes():
+ output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 10.10.10.1/32 json"))
+ expected = {
+ "paths": [
+ {
+ "community": {
+ "string": "65000:1",
+ }
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_check_received_routes,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "10.10.10.1/32 does not have 65000:1 community attached"
+
+
+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..e763072
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_track_peer/test_bgp_conditional_advertisement_track_peer.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+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_confed1/__init__.py b/tests/topotests/bgp_confed1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_confed1/__init__.py
diff --git a/tests/topotests/bgp_confed1/r1/bgp_ipv4_unicast.json b/tests/topotests/bgp_confed1/r1/bgp_ipv4_unicast.json
new file mode 100644
index 0000000..d3988eb
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r1/bgp_ipv4_unicast.json
@@ -0,0 +1,63 @@
+{
+ "vrfId":0,
+ "vrfName":"default",
+ "routerId":"203.0.113.1",
+ "defaultLocPrf":100,
+ "localAS":100,
+ "routes":{"203.0.113.0/28":[
+ {
+ "network":"203.0.113.0\/28",
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.16/28":[
+ {
+ "network":"203.0.113.16\/28",
+ "peerId":"192.0.2.2",
+ "path":"300",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.32/28":[
+ {
+ "network":"203.0.113.32\/28",
+ "peerId":"192.0.2.2",
+ "path":"300",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.48/28":[
+ {
+ "network":"203.0.113.48\/28",
+ "peerId":"192.0.2.2",
+ "path":"300 400",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp_confed1/r1/bgp_summary.json b/tests/topotests/bgp_confed1/r1/bgp_summary.json
new file mode 100644
index 0000000..f999021
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r1/bgp_summary.json
@@ -0,0 +1,13 @@
+{
+ "ipv4Unicast":{
+ "routerId":"203.0.113.1",
+ "as":100,
+ "peers":{
+ "192.0.2.2":{
+ "remoteAs": 300,
+ "state": "Established",
+ "peerState":"OK"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_confed1/r1/bgpd.conf b/tests/topotests/bgp_confed1/r1/bgpd.conf
new file mode 100644
index 0000000..107d2ad
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r1/bgpd.conf
@@ -0,0 +1,13 @@
+!debug bgp neighbor-events
+!debug bgp nht
+!debug bgp updates in
+!debug bgp updates out
+!
+router bgp 100
+ no bgp ebgp-requires-policy
+!
+ neighbor 192.0.2.2 remote-as 300
+ address-family ipv4 unicast
+ network 203.0.113.0/28
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_confed1/r1/isisd.conf b/tests/topotests/bgp_confed1/r1/isisd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r1/isisd.conf
diff --git a/tests/topotests/bgp_confed1/r1/zebra.conf b/tests/topotests/bgp_confed1/r1/zebra.conf
new file mode 100644
index 0000000..5f76e74
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r1/zebra.conf
@@ -0,0 +1,6 @@
+interface r1-eth0
+ ip address 192.0.2.1/28
+!
+interface lo
+ ip address 203.0.113.1/28
+!
diff --git a/tests/topotests/bgp_confed1/r2/bgp_ipv4_unicast.json b/tests/topotests/bgp_confed1/r2/bgp_ipv4_unicast.json
new file mode 100644
index 0000000..b4a0946
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r2/bgp_ipv4_unicast.json
@@ -0,0 +1,63 @@
+{
+ "vrfId":0,
+ "vrfName":"default",
+ "routerId":"203.0.113.17",
+ "defaultLocPrf":100,
+ "localAS":200,
+ "routes":{"203.0.113.0/28": [
+ {
+ "network":"203.0.113.0\/28",
+ "peerId":"192.0.2.1",
+ "path":"100",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.16/28":[
+ {
+ "network":"203.0.113.16\/28",
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.32/28":[
+ {
+ "network":"203.0.113.32\/28",
+ "peerId":"192.0.2.18",
+ "path":"(300)",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.18",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.48/28":[
+ {
+ "network":"203.0.113.48\/28",
+ "peerId":"192.0.2.18",
+ "path":"(300) 400",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.50",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp_confed1/r2/bgp_summary.json b/tests/topotests/bgp_confed1/r2/bgp_summary.json
new file mode 100644
index 0000000..c2690a1
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r2/bgp_summary.json
@@ -0,0 +1,18 @@
+{
+ "ipv4Unicast":{
+ "routerId":"203.0.113.17",
+ "as":200,
+ "peers":{
+ "192.0.2.1":{
+ "remoteAs":100,
+ "state":"Established",
+ "peerState":"OK"
+ },
+ "192.0.2.18":{
+ "remoteAs":300,
+ "state":"Established",
+ "peerState":"OK"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_confed1/r2/bgpd.conf b/tests/topotests/bgp_confed1/r2/bgpd.conf
new file mode 100644
index 0000000..fe13dfe
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r2/bgpd.conf
@@ -0,0 +1,18 @@
+!debug bgp neighbor-events
+!debug bgp nht
+!debug bgp updates in
+!debug bgp updates out
+!
+router bgp 200
+ no bgp ebgp-requires-policy
+ bgp confederation identifier 300
+ bgp confederation peers 300
+ neighbor 192.0.2.1 remote-as 100
+ neighbor 192.0.2.18 remote-as 300
+ !
+ address-family ipv4 unicast
+ network 203.0.113.16/28
+ neighbor 192.0.2.18 default-originate
+ exit-address-family
+!
+
diff --git a/tests/topotests/bgp_confed1/r2/isisd.conf b/tests/topotests/bgp_confed1/r2/isisd.conf
new file mode 100644
index 0000000..135bb00
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r2/isisd.conf
@@ -0,0 +1,8 @@
+interface r2-eth1
+ ip router isis 1
+ isis circuit-type level-2-only
+
+router isis 1
+ is-type level-2-only
+ net 49.0001.0002.0002.0002.00
+ redistribute ipv4 connected level-2
diff --git a/tests/topotests/bgp_confed1/r2/zebra.conf b/tests/topotests/bgp_confed1/r2/zebra.conf
new file mode 100644
index 0000000..85ebe9e
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r2/zebra.conf
@@ -0,0 +1,9 @@
+interface r2-eth0
+ ip address 192.0.2.2/28
+!
+interface r2-eth1
+ ip address 192.0.2.17/28
+!
+interface lo
+ ip address 203.0.113.17/28
+!
diff --git a/tests/topotests/bgp_confed1/r3/bgp_ipv4_unicast.json b/tests/topotests/bgp_confed1/r3/bgp_ipv4_unicast.json
new file mode 100644
index 0000000..a263a9f
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r3/bgp_ipv4_unicast.json
@@ -0,0 +1,77 @@
+{
+ "vrfId":0,
+ "vrfName":"default",
+ "routerId":"203.0.113.33",
+ "defaultLocPrf":100,
+ "localAS":300,
+ "routes":{"0.0.0.0/0":[
+ {
+ "network":"0.0.0.0\/0",
+ "peerId":"192.0.2.17",
+ "path":"(200)",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.17",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.0/28":[
+ {
+ "network":"203.0.113.0\/28",
+ "peerId":"192.0.2.17",
+ "path":"(200) 100",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.16/28":[
+ {
+ "network":"203.0.113.16\/28",
+ "peerId":"192.0.2.17",
+ "path":"(200)",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.17",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.32/28":[
+ {
+ "network":"203.0.113.32\/28",
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.48/28":[
+ {
+ "network":"203.0.113.48\/28",
+ "peerId":"192.0.2.50",
+ "path":"400",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.50",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp_confed1/r3/bgp_summary.json b/tests/topotests/bgp_confed1/r3/bgp_summary.json
new file mode 100644
index 0000000..0cc0d53
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r3/bgp_summary.json
@@ -0,0 +1,18 @@
+{
+ "ipv4Unicast":{
+ "routerId":"203.0.113.33",
+ "as":300,
+ "peers":{
+ "192.0.2.17":{
+ "remoteAs":200,
+ "state":"Established",
+ "peerState":"OK"
+ },
+ "192.0.2.50":{
+ "remoteAs":400,
+ "state":"Established",
+ "peerState":"OK"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_confed1/r3/bgpd.conf b/tests/topotests/bgp_confed1/r3/bgpd.conf
new file mode 100644
index 0000000..74d5fd6
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r3/bgpd.conf
@@ -0,0 +1,17 @@
+!debug bgp neighbor-events
+!debug bgp nht
+!debug bgp updates in
+!debug bgp updates out
+!
+router bgp 300
+ no bgp ebgp-requires-policy
+ bgp confederation identifier 300
+ bgp confederation peers 200
+ neighbor 192.0.2.17 remote-as 200
+ neighbor 192.0.2.50 remote-as 400
+!
+ address-family ipv4 unicast
+ network 203.0.113.32/28
+ exit-address-family
+!
+
diff --git a/tests/topotests/bgp_confed1/r3/isisd.conf b/tests/topotests/bgp_confed1/r3/isisd.conf
new file mode 100644
index 0000000..a0b1200
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r3/isisd.conf
@@ -0,0 +1,8 @@
+interface r3-eth1
+ ip router isis 1
+ isis circuit-type level-2-only
+
+router isis 1
+ is-type level-2-only
+ net 49.0001.0003.0003.0003.00
+ redistribute ipv4 connected level-2
diff --git a/tests/topotests/bgp_confed1/r3/zebra.conf b/tests/topotests/bgp_confed1/r3/zebra.conf
new file mode 100644
index 0000000..555c8ed
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r3/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface r3-eth0
+ ip address 192.0.2.49/28
+!
+interface r3-eth1
+ ip address 192.0.2.18/28
+!
+interface lo
+ ip address 203.0.113.33/28
+!
diff --git a/tests/topotests/bgp_confed1/r4/bgp_ipv4_unicast.json b/tests/topotests/bgp_confed1/r4/bgp_ipv4_unicast.json
new file mode 100644
index 0000000..8a1f0b6
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r4/bgp_ipv4_unicast.json
@@ -0,0 +1,77 @@
+{
+ "vrfId":0,
+ "vrfName":"default",
+ "routerId":"203.0.113.49",
+ "defaultLocPrf":100,
+ "localAS":400,
+ "routes":{"0.0.0.0/0":[
+ {
+ "network":"0.0.0.0\/0",
+ "peerId":"192.0.2.49",
+ "path":"300",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.49",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.0/28":[
+ {
+ "network":"203.0.113.0\/28",
+ "peerId":"192.0.2.49",
+ "path":"300 100",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.49",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.16/28":[
+ {
+ "network":"203.0.113.16\/28",
+ "peerId":"192.0.2.49",
+ "path":"300",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.49",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.32/28":[
+ {
+ "network":"203.0.113.32\/28",
+ "peerId":"192.0.2.49",
+ "path":"300",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.0.2.49",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"203.0.113.48/28":[
+ {
+ "network":"203.0.113.48\/28",
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp_confed1/r4/bgp_summary.json b/tests/topotests/bgp_confed1/r4/bgp_summary.json
new file mode 100644
index 0000000..11a0c45
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r4/bgp_summary.json
@@ -0,0 +1,13 @@
+{
+ "ipv4Unicast":{
+ "routerId":"203.0.113.49",
+ "as":400,
+ "peers":{
+ "192.0.2.49":{
+ "remoteAs":300,
+ "state":"Established",
+ "peerState":"OK"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_confed1/r4/bgpd.conf b/tests/topotests/bgp_confed1/r4/bgpd.conf
new file mode 100644
index 0000000..89a85e5
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r4/bgpd.conf
@@ -0,0 +1,14 @@
+!debug bgp neighbor-events
+!debug bgp nht
+!debug bgp updates in
+!debug bgp updates out
+!
+router bgp 400
+ no bgp ebgp-requires-policy
+ bgp disable-ebgp-connected-route-check
+!
+ neighbor 192.0.2.49 remote-as 300
+ address-family ipv4 unicast
+ network 203.0.113.48/28
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_confed1/r4/isisd.conf b/tests/topotests/bgp_confed1/r4/isisd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r4/isisd.conf
diff --git a/tests/topotests/bgp_confed1/r4/zebra.conf b/tests/topotests/bgp_confed1/r4/zebra.conf
new file mode 100644
index 0000000..28c448e
--- /dev/null
+++ b/tests/topotests/bgp_confed1/r4/zebra.conf
@@ -0,0 +1,6 @@
+interface r4-eth0
+ ip address 192.0.2.50/28
+!
+interface lo
+ ip address 203.0.113.49/28
+!
diff --git a/tests/topotests/bgp_confed1/test_bgp_confed1.py b/tests/topotests/bgp_confed1/test_bgp_confed1.py
new file mode 100644
index 0000000..57a8522
--- /dev/null
+++ b/tests/topotests/bgp_confed1/test_bgp_confed1.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_confed1.py
+#
+# Copyright 2022 6WIND S.A.
+#
+
+"""
+test_bgp_confed1.py: Test the FRR BGP confederations with AS member
+same as confederation Id, verify BGP prefixes and path distribution
+"""
+
+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]
+
+
+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("s3")
+ 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):
+
+ 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))
+ )
+ 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_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=2.0)
+ assertmsg = "{}: bgp did not converge".format(router.name)
+ assert res is None, assertmsg
+
+
+def test_bgp_confed_ipv4_unicast():
+ "Assert that BGP is exchanging BGP route."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bgp peers exchanging UPDATES")
+
+ for router in tgen.routers().values():
+ ref_file = "{}/{}/bgp_ipv4_unicast.json".format(CWD, router.name)
+ expected = json.loads(open(ref_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bgp ipv4 unicast json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=40, wait=2.5)
+ assertmsg = "{}: BGP UPDATE exchange failure".format(router.name)
+ assert res is None, assertmsg
+
+
+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..a0014c7
--- /dev/null
+++ b/tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..75e6656
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/test_bgp_default_originate_2links.py
@@ -0,0 +1,1808 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Shreenidhi A R <rshreenidhi@vmware.com>
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+"""
+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
+from time import sleep
+
+# 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 import topotest
+
+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)
+ sleep(1)
+
+ 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)
+ sleep(1)
+
+ 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"]
+
+ def ping_router():
+ output = router.run("ping -c 4 -w 4 {}".format(pingaddr))
+ logger.info(output)
+ if " 0% packet loss" not in output:
+ return False
+
+ _, res = topotest.run_and_expect(ping_router, None, count=10, wait=1)
+ 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..6156968
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_1.py
@@ -0,0 +1,2527 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Shreenidhi A R <rshreenidhi@vmware.com>
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+"""
+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..59f833b
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_2.py
@@ -0,0 +1,2427 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Shreenidhi A R <rshreenidhi@vmware.com>
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+"""
+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..eca41e3
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_3.py
@@ -0,0 +1,2528 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Shreenidhi A R <rshreenidhi@vmware.com>
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+"""
+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..4dedac5
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/test_default_orginate_vrf.py
@@ -0,0 +1,1377 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Shreenidhi A R <rshreenidhi@vmware.com>
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+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..82c4e7e
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/test_default_originate_conditional_routemap.py
@@ -0,0 +1,2092 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Shreenidhi A R <rshreenidhi@vmware.com>
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+"""
+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_originate_timer/__init__.py b/tests/topotests/bgp_default_originate_timer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_timer/__init__.py
diff --git a/tests/topotests/bgp_default_originate_timer/r1/bgpd.conf b/tests/topotests/bgp_default_originate_timer/r1/bgpd.conf
new file mode 100644
index 0000000..f2a1c90
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_timer/r1/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ bgp default-originate timer 3600
+ 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
+ address-family ipv4
+ neighbor 192.168.1.2 default-originate route-map default
+ exit-address-family
+!
+bgp community-list standard r3 seq 5 permit 65003:1
+!
+route-map default permit 10
+ match community r3
+exit
diff --git a/tests/topotests/bgp_default_originate_timer/r1/zebra.conf b/tests/topotests/bgp_default_originate_timer/r1/zebra.conf
new file mode 100644
index 0000000..3692361
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_timer/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+interface r1-eth1
+ ip address 192.168.2.1/24
+!
diff --git a/tests/topotests/bgp_default_originate_timer/r2/bgpd.conf b/tests/topotests/bgp_default_originate_timer/r2/bgpd.conf
new file mode 100644
index 0000000..7ca65a9
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_timer/r2/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65002
+ 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
+!
diff --git a/tests/topotests/bgp_default_originate_timer/r2/zebra.conf b/tests/topotests/bgp_default_originate_timer/r2/zebra.conf
new file mode 100644
index 0000000..0c95656
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_timer/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_default_originate_timer/r3/bgpd.conf b/tests/topotests/bgp_default_originate_timer/r3/bgpd.conf
new file mode 100644
index 0000000..0a37913
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_timer/r3/bgpd.conf
@@ -0,0 +1,12 @@
+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
+ address-family ipv4 unicast
+ redistribute connected route-map r1
+ exit-address-family
+!
+route-map r1 permit 10
+ set community 65003:1
+exit
diff --git a/tests/topotests/bgp_default_originate_timer/r3/zebra.conf b/tests/topotests/bgp_default_originate_timer/r3/zebra.conf
new file mode 100644
index 0000000..20801f9
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_timer/r3/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface lo
+ ip address 10.10.10.10/32
+!
+interface r3-eth0
+ ip address 192.168.2.2/24
+!
diff --git a/tests/topotests/bgp_default_originate_timer/test_bgp_default_originate_timer.py b/tests/topotests/bgp_default_originate_timer/test_bgp_default_originate_timer.py
new file mode 100644
index 0000000..b2ba936
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_timer/test_bgp_default_originate_timer.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Check if `bgp default-originate timer` commands takes an effect:
+1. Set bgp default-originate timer 3600
+2. No default route is advertised because the timer is running for 3600 seconds
+3. We reduce it to 10 seconds
+4. Default route is advertised
+"""
+
+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, 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["r1"])
+ 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_default_originate_timer():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ r3 = tgen.gears["r3"]
+
+ def _bgp_default_received_from_r1():
+ output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast 0.0.0.0/0 json"))
+ expected = {
+ "paths": [
+ {
+ "nexthops": [
+ {
+ "hostname": "r1",
+ "ip": "192.168.1.1",
+ }
+ ],
+ }
+ ],
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_default_received_from_r1)
+ _, result = topotest.run_and_expect(test_func, not None, count=30, wait=1)
+ assert result is not None, "Seen default route received from r1, but should not"
+
+ step("Set BGP default-originate timer to 10 seconds")
+ r1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ bgp default-originate timer 10
+ """
+ )
+
+ step("Trigger BGP UPDATE from r3")
+ r3.vtysh_cmd(
+ """
+ configure terminal
+ route-map r1 permit 10
+ set metric 1
+ """
+ )
+
+ test_func = functools.partial(_bgp_default_received_from_r1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Did not see default route received from r1, but should"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_default_originate_withdraw/__init__.py b/tests/topotests/bgp_default_originate_withdraw/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_withdraw/__init__.py
diff --git a/tests/topotests/bgp_default_originate_withdraw/r1/bgpd.conf b/tests/topotests/bgp_default_originate_withdraw/r1/bgpd.conf
new file mode 100644
index 0000000..6813b02
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_withdraw/r1/bgpd.conf
@@ -0,0 +1,12 @@
+router bgp 65001
+ 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
+ address-family ipv4
+ neighbor 192.168.1.2 default-originate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_default_originate_withdraw/r1/zebra.conf b/tests/topotests/bgp_default_originate_withdraw/r1/zebra.conf
new file mode 100644
index 0000000..3692361
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_withdraw/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+interface r1-eth1
+ ip address 192.168.2.1/24
+!
diff --git a/tests/topotests/bgp_default_originate_withdraw/r2/bgpd.conf b/tests/topotests/bgp_default_originate_withdraw/r2/bgpd.conf
new file mode 100644
index 0000000..60e6236
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_withdraw/r2/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65002
+ 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
+ network 192.168.2.0/24
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_default_originate_withdraw/r2/zebra.conf b/tests/topotests/bgp_default_originate_withdraw/r2/zebra.conf
new file mode 100644
index 0000000..0c95656
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_withdraw/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_default_originate_withdraw/r3/bgpd.conf b/tests/topotests/bgp_default_originate_withdraw/r3/bgpd.conf
new file mode 100644
index 0000000..547cf86
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_withdraw/r3/bgpd.conf
@@ -0,0 +1,9 @@
+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
+ address-family ipv4 unicast
+ network 0.0.0.0/0
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_default_originate_withdraw/r3/zebra.conf b/tests/topotests/bgp_default_originate_withdraw/r3/zebra.conf
new file mode 100644
index 0000000..7ccdcfd
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_withdraw/r3/zebra.conf
@@ -0,0 +1,5 @@
+!
+interface r3-eth0
+ ip address 192.168.2.2/24
+!
+ip route 0.0.0.0/0 Null0
diff --git a/tests/topotests/bgp_default_originate_withdraw/test_bgp_default_originate_withdraw.py b/tests/topotests/bgp_default_originate_withdraw/test_bgp_default_originate_withdraw.py
new file mode 100644
index 0000000..e25f85a
--- /dev/null
+++ b/tests/topotests/bgp_default_originate_withdraw/test_bgp_default_originate_withdraw.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Check if bgpd do not crash if we use default-originate while
+received a default route from the neighbor as well. 0.0.0.0/0
+MUST be kept in RIB even if we remove default-originate from
+the neighbor.
+"""
+
+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, 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["r1"])
+ 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_default_originate_with_default_received():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ def _bgp_default_received_from_r3():
+ output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 0.0.0.0/0 json"))
+ expected = {
+ "paths": [
+ {
+ "nexthops": [
+ {
+ "hostname": "r3",
+ "ip": "192.168.2.2",
+ }
+ ],
+ }
+ ],
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_default_received_from_r3)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Cannot see default route received from r3"
+
+ def _bgp_advertised_default_originate_to_r2():
+ output = json.loads(
+ r1.vtysh_cmd(
+ "show bgp ipv4 unicast neighbors 192.168.1.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "bgpOriginatingDefaultNetwork": "0.0.0.0/0",
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_advertised_default_originate_to_r2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Cannot see default-originate route advertised to r2"
+
+ step("Disable default-originate for r2")
+ r1.vtysh_cmd(
+ """
+ configure
+ router bgp
+ address-family ipv4 unicast
+ no neighbor 192.168.1.2 default-originate
+ """
+ )
+
+ def _bgp_advertised_default_from_r3_to_r2():
+ output = json.loads(
+ r1.vtysh_cmd(
+ "show bgp ipv4 unicast neighbors 192.168.1.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "bgpOriginatingDefaultNetwork": None,
+ "advertisedRoutes": {
+ "0.0.0.0/0": {
+ "valid": True,
+ }
+ },
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_advertised_default_from_r3_to_r2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Cannot see default route advertised to r2"
+
+ step("Enable default-originate for r2")
+ r1.vtysh_cmd(
+ """
+ configure
+ router bgp
+ address-family ipv4 unicast
+ neighbor 192.168.1.2 default-originate
+ do clear ip bgp *
+ """
+ )
+
+ step("Check if default-originate route advertised to r2")
+ test_func = functools.partial(_bgp_advertised_default_originate_to_r2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Cannot see default-originate route advertised to r2"
+
+
+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..2463b05
--- /dev/null
+++ b/tests/topotests/bgp_default_route/test_bgp_default-originate.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2019-2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..9dcb5a1
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match/test_bgp_default-originate_route-map_match.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2019-2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..965d348
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..f94620b
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..3bd900b
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2019-2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..44b009e
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/r1/bgpd.conf
@@ -0,0 +1,10 @@
+!
+router bgp 65001
+ timers bgp 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..8274e3f
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/r2/bgpd.conf
@@ -0,0 +1,13 @@
+router bgp 65002
+ timers bgp 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..98eb2e1
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/r3/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65003
+ timers bgp 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..68245c4
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/r4/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65004
+ timers bgp 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..70562ce
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/test_disable_addpath_rx.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..5528b59
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json
@@ -0,0 +1,404 @@
+{
+ "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": {
+ "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": {
+
+ "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": {
+
+ "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": {
+
+ "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": {
+
+ "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..0bd3d28
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/test_bgp_admin_dist.py
@@ -0,0 +1,1269 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+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..bf30f2e
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/test_bgp_admin_dist_vrf.py
@@ -0,0 +1,887 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+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..2ca50aa
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/test_bgp_distance_change.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# bgp_distance_change.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..2f76d59
--- /dev/null
+++ b/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf
@@ -0,0 +1,12 @@
+!
+!debug bgp neighbor
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ bgp default show-hostname
+ bgp default show-nexthop-hostname
+ 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.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..06e1e54
--- /dev/null
+++ b/tests/topotests/bgp_dont_capability_negotiate/r2/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65002
+ 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
+ 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..ab71f87
--- /dev/null
+++ b/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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
+from lib.common_config import step
+
+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 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)
+
+
+def test_bgp_dont_capability_negotiate():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ 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 converge with dont-capability-negotiate"
+
+
+def test_bgp_check_fqdn():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _bgp_check_fqdn(fqdn=None):
+ output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 172.16.16.1/32 json"))
+ expected = {
+ "paths": [
+ {
+ "nexthops": [
+ {
+ "hostname": fqdn,
+ }
+ ],
+ "peer": {
+ "hostname": fqdn,
+ },
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Enable all capabilities")
+ r1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ address-family ipv4 unicast
+ no neighbor 192.168.1.2 dont-capability-negotiate
+ end
+ clear bgp 192.168.1.2
+ """
+ )
+
+ step("Wait to converge")
+ 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 converge with dont-capability-negotiate"
+
+ test_func = functools.partial(_bgp_check_fqdn, "r2")
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "FQDN capability enabled, but r1 can't see it"
+
+ step("Disable sending any capabilities from r2")
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ address-family ipv4 unicast
+ neighbor 192.168.1.1 dont-capability-negotiate
+ end
+ clear bgp 192.168.1.1
+ """
+ )
+
+ step("Wait to converge")
+ 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 converge with dont-capability-negotiate"
+
+ step("Make sure FQDN capability is reset")
+ test_func = functools.partial(_bgp_check_fqdn)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "FQDN capability disabled, but we still have a hostname"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_dynamic_capability/__init__.py b/tests/topotests/bgp_dynamic_capability/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_dynamic_capability/__init__.py
diff --git a/tests/topotests/bgp_dynamic_capability/r1/frr.conf b/tests/topotests/bgp_dynamic_capability/r1/frr.conf
new file mode 100644
index 0000000..50280a9
--- /dev/null
+++ b/tests/topotests/bgp_dynamic_capability/r1/frr.conf
@@ -0,0 +1,15 @@
+!
+!debug bgp neighbor
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ bgp graceful-restart
+ bgp long-lived stale-time 10
+ 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.1.2 capability dynamic
+!
diff --git a/tests/topotests/bgp_dynamic_capability/r2/frr.conf b/tests/topotests/bgp_dynamic_capability/r2/frr.conf
new file mode 100644
index 0000000..16b83a5
--- /dev/null
+++ b/tests/topotests/bgp_dynamic_capability/r2/frr.conf
@@ -0,0 +1,22 @@
+!
+!debug bgp neighbor
+!
+int lo
+ ip address 10.10.10.10/32
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+router bgp 65002
+ bgp graceful-restart
+ bgp long-lived stale-time 20
+ 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
+ neighbor 192.168.1.1 capability dynamic
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py
new file mode 100644
index 0000000..75c712e
--- /dev/null
+++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py
@@ -0,0 +1,213 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if BGP graceful restart / long-lived graceful restart capabilities
+(restart time, stale time and notification flag) are exchanged dynamically
+via BGP dynamic capability.
+"""
+
+import os
+import re
+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 step
+
+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 _, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_dynamic_capability_graceful_restart():
+ 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(r1.vtysh_cmd("show bgp neighbor json"))
+ expected = {
+ "192.168.1.2": {
+ "bgpState": "Established",
+ "neighborCapabilities": {
+ "dynamic": "advertisedAndReceived",
+ "gracefulRestart": "advertisedAndReceived",
+ "longLivedGracefulRestart": "advertisedAndReceived",
+ },
+ "addressFamilyInfo": {
+ "ipv4Unicast": {
+ "acceptedPrefixCounter": 2,
+ }
+ },
+ "gracefulRestartInfo": {
+ "nBit": True,
+ "timers": {
+ "receivedRestartTimer": 120,
+ "configuredLlgrStaleTime": 10,
+ },
+ "ipv4Unicast": {
+ "timers": {
+ "llgrStaleTime": 10,
+ }
+ },
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_converge,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Can't converge"
+
+ step(
+ "Change Graceful-Restart restart-time, LLGR stale-time and check if they changed dynamically"
+ )
+
+ # Clear message stats to check if we receive a notification or not after we
+ # change the settings fo LLGR.
+ r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats")
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ bgp graceful-restart restart-time 123
+ bgp long-lived-graceful-restart stale-time 5
+ """
+ )
+
+ def _bgp_check_if_session_not_reset_after_changing_gr_settings():
+ output = json.loads(r1.vtysh_cmd("show bgp neighbor json"))
+ expected = {
+ "192.168.1.2": {
+ "bgpState": "Established",
+ "neighborCapabilities": {
+ "dynamic": "advertisedAndReceived",
+ "gracefulRestart": "advertisedAndReceived",
+ "longLivedGracefulRestart": "advertisedAndReceived",
+ },
+ "addressFamilyInfo": {
+ "ipv4Unicast": {
+ "acceptedPrefixCounter": 2,
+ }
+ },
+ "gracefulRestartInfo": {
+ "nBit": True,
+ "timers": {
+ "receivedRestartTimer": 123,
+ "configuredLlgrStaleTime": 10,
+ },
+ "ipv4Unicast": {
+ "timers": {
+ "llgrStaleTime": 5,
+ }
+ },
+ },
+ "messageStats": {
+ "notificationsRecv": 0,
+ "capabilityRecv": 2,
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_check_if_session_not_reset_after_changing_gr_settings,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert (
+ result is None
+ ), "Session was reset after changing Graceful-Restart restart-time"
+
+ step(
+ "Disable Graceful-Restart notification support, and check if it's changed dynamically"
+ )
+
+ # Clear message stats to check if we receive a notification or not after we
+ # change the settings fo LLGR.
+ r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats")
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ no bgp graceful-restart notification
+ """
+ )
+
+ def _bgp_check_if_session_not_reset_after_changing_notification():
+ output = json.loads(r1.vtysh_cmd("show bgp neighbor json"))
+ expected = {
+ "192.168.1.2": {
+ "bgpState": "Established",
+ "neighborCapabilities": {
+ "dynamic": "advertisedAndReceived",
+ "gracefulRestart": "advertisedAndReceived",
+ "longLivedGracefulRestart": "advertisedAndReceived",
+ },
+ "gracefulRestartInfo": {
+ "nBit": False,
+ "timers": {
+ "receivedRestartTimer": 123,
+ "configuredLlgrStaleTime": 10,
+ },
+ "ipv4Unicast": {
+ "timers": {
+ "llgrStaleTime": 5,
+ }
+ },
+ },
+ "messageStats": {
+ "notificationsRecv": 0,
+ "capabilityRecv": 1,
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_check_if_session_not_reset_after_changing_notification,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert (
+ result is None
+ ), "Session was reset after changing Graceful-Restart notification support"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py
new file mode 100644
index 0000000..576ef74
--- /dev/null
+++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_role.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if role capability is exchanged dynamically.
+"""
+
+import os
+import re
+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 step
+
+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 _, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_dynamic_capability_role():
+ 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(r1.vtysh_cmd("show bgp neighbor json"))
+ expected = {
+ "192.168.1.2": {
+ "bgpState": "Established",
+ "localRole": "undefined",
+ "remoteRole": "undefined",
+ "neighborCapabilities": {
+ "dynamic": "advertisedAndReceived",
+ },
+ "addressFamilyInfo": {
+ "ipv4Unicast": {
+ "acceptedPrefixCounter": 2,
+ }
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_converge,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Can't converge"
+
+ step("Set local-role and check if it's exchanged dynamically")
+
+ # Clear message stats to check if we receive a notification or not after we
+ # change the settings fo LLGR.
+ r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats")
+ r1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ neighbor 192.168.1.2 local-role customer
+ """
+ )
+
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ neighbor 192.168.1.1 local-role provider
+ """
+ )
+
+ def _bgp_check_if_session_not_reset():
+ output = json.loads(r1.vtysh_cmd("show bgp neighbor json"))
+ expected = {
+ "192.168.1.2": {
+ "bgpState": "Established",
+ "localRole": "customer",
+ "remoteRole": "provider",
+ "neighborCapabilities": {
+ "dynamic": "advertisedAndReceived",
+ "role": "advertisedAndReceived",
+ },
+ "addressFamilyInfo": {
+ "ipv4Unicast": {
+ "acceptedPrefixCounter": 2,
+ }
+ },
+ "messageStats": {
+ "notificationsRecv": 0,
+ "capabilityRecv": 1,
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_check_if_session_not_reset,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Session was reset after setting role capability"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py
new file mode 100644
index 0000000..002d782
--- /dev/null
+++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_software_version.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if software version capability is exchanged dynamically.
+"""
+
+import os
+import re
+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 step
+
+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 _, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_dynamic_capability_software_version():
+ 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(r1.vtysh_cmd("show bgp neighbor json"))
+ expected = {
+ "192.168.1.2": {
+ "bgpState": "Established",
+ "neighborCapabilities": {
+ "dynamic": "advertisedAndReceived",
+ "softwareVersion": {
+ "advertisedSoftwareVersion": None,
+ "receivedSoftwareVersion": None,
+ },
+ },
+ "addressFamilyInfo": {
+ "ipv4Unicast": {
+ "acceptedPrefixCounter": 2,
+ }
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_converge,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Can't converge"
+
+ step("Enable software version capability and check if it's exchanged dynamically")
+
+ # Clear message stats to check if we receive a notification or not after we
+ # change the settings fo LLGR.
+ r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats")
+ r1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ neighbor 192.168.1.2 capability software-version
+ """
+ )
+
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ neighbor 192.168.1.1 capability software-version
+ """
+ )
+
+ def _bgp_check_if_session_not_reset():
+ def _bgp_software_version():
+ try:
+ versions = output["192.168.1.2"]["neighborCapabilities"][
+ "softwareVersion"
+ ]
+ adv = versions["advertisedSoftwareVersion"]
+ rcv = versions["receivedSoftwareVersion"]
+
+ if not adv and not rcv:
+ return ""
+
+ pattern = "^FRRouting/\\d.+"
+ if re.search(pattern, adv) and re.search(pattern, rcv):
+ return adv, rcv
+ except:
+ return ""
+
+ output = json.loads(r1.vtysh_cmd("show bgp neighbor json"))
+ adv, rcv = _bgp_software_version()
+ expected = {
+ "192.168.1.2": {
+ "bgpState": "Established",
+ "neighborCapabilities": {
+ "dynamic": "advertisedAndReceived",
+ "softwareVersion": {
+ "advertisedSoftwareVersion": adv,
+ "receivedSoftwareVersion": rcv,
+ },
+ },
+ "addressFamilyInfo": {
+ "ipv4Unicast": {
+ "acceptedPrefixCounter": 2,
+ }
+ },
+ "messageStats": {
+ "notificationsRecv": 0,
+ "capabilityRecv": 1,
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_check_if_session_not_reset,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert (
+ result is None
+ ), "Session was reset after enabling software version capability"
+
+
+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..34f7dc8
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..6e3b285
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# bgp_ebgp_requires_policy.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..97751ec
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python2
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_ecmp_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..8362776
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py
@@ -0,0 +1,746 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""
+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, kernel version should be >=4.15")
+
+ 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 Expected: Routes still present in {} RIB. Found: {}".format(
+ tc_name, dut, result
+ )
+
+ 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 Expected: Routes still present in {} RIB. Found: {}".format(
+ tc_name, dut, result
+ )
+
+ 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 Expected: Routes still present in {} RIB. Found: {}".format(
+ tc_name, dut, result
+ )
+
+ 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..b347042
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py
@@ -0,0 +1,747 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""
+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, kernel version should be >=4.15")
+
+ 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 Expected: Routes still present in {} RIB. Found: {}".format(
+ tc_name, dut, result
+ )
+
+ 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 Expected: Routes still present in {} RIB. Found: {}".format(
+ tc_name, dut, result
+ )
+
+ 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 Expected: Routes still present in {} RIB. Found: {}".format(
+ tc_name, dut, result
+ )
+
+ 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..366cf3d
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py
@@ -0,0 +1,270 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""
+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 Topogen, get_topogen
+
+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,
+ start_topology,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+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)
+
+ # This function initiates the topology build with Topogen...
+ json_file = "{}/ibgp_ecmp_topo3.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)
+
+ # 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 "
+ "Expected: BGP should not be converged for {} \n "
+ "Found: {}".format(tc_name, "r2", 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 "
+ "Expected: BGP should not be converged for {} \n "
+ "Found: {}".format(tc_name, "r2", 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/leaf1/evpn.conf b/tests/topotests/bgp_evpn_mh/leaf1/evpn.conf
new file mode 100644
index 0000000..33b6d08
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/leaf1/evpn.conf
@@ -0,0 +1,21 @@
+frr defaults datacenter
+!
+router bgp 65101
+ bgp router-id 192.168.100.13
+ no bgp ebgp-requires-policy
+ neighbor 192.168.50.1 remote-as external
+ neighbor 192.168.51.1 remote-as external
+ 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.50.1 activate
+ neighbor 192.168.51.1 activate
+ 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/leaf1/pim.conf b/tests/topotests/bgp_evpn_mh/leaf1/pim.conf
new file mode 100644
index 0000000..a4de64d
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/leaf1/pim.conf
@@ -0,0 +1,26 @@
+#debug pim packet
+#debug pim packet register
+#debug pim event
+#debug pim trace
+ip pim ecmp
+ip pim rp 192.168.100.13
+ip pim spt-switchover infinity-and-beyond
+!
+int leaf1-eth0
+ ip pim
+!
+int leaf1-eth1
+ ip pim
+!
+int leaf1-eth2
+ ip pim
+!
+int leaf1-eth3
+ ip pim
+!
+int leaf1-eth4
+ ip pim
+!
+int leaf1-eth5
+ ip pim
+!
diff --git a/tests/topotests/bgp_evpn_mh/leaf1/zebra.conf b/tests/topotests/bgp_evpn_mh/leaf1/zebra.conf
new file mode 100644
index 0000000..f666f98
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/leaf1/zebra.conf
@@ -0,0 +1,30 @@
+# spine 1 connection
+int leaf1-eth0
+ description spine1 connection
+ ip addr 192.168.50.2/24
+
+#spine 2 connection
+int leaf1-eth1
+ description spine2 connection
+ ip addr 192.168.51.2/24
+
+#torm11 connection
+int leaf1-eth2
+ description torm11 connection
+ ip addr 192.168.1.1/24
+!
+#torm12 connection
+int leaf1-eth3
+ description torm12 connection
+ ip addr 192.168.2.1/24
+!
+#torm21 connection
+int leaf1-eth4
+ description torm21 connection
+ ip addr 192.168.3.1/24
+!
+#torm22 connection
+int leaf1-eth5
+ descriptoin torm22 connection
+ ip addr 192.168.4.1/24
+!
diff --git a/tests/topotests/bgp_evpn_mh/leaf2/evpn.conf b/tests/topotests/bgp_evpn_mh/leaf2/evpn.conf
new file mode 100644
index 0000000..428998b
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/leaf2/evpn.conf
@@ -0,0 +1,21 @@
+frr defaults datacenter
+!
+router bgp 65101
+ bgp router-id 192.168.100.14
+ no bgp ebgp-requires-policy
+ neighbor 192.168.61.1 remote-as external
+ neighbor 192.168.51.1 remote-as external
+ 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.61.1 activate
+ neighbor 192.168.51.1 activate
+ 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/leaf2/pim.conf b/tests/topotests/bgp_evpn_mh/leaf2/pim.conf
new file mode 100644
index 0000000..3abd969
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/leaf2/pim.conf
@@ -0,0 +1,20 @@
+#debug pim packet register
+#debug pim packet
+#debug pim event
+#debug pim trace
+ip pim ecmp
+ip pim rp 192.168.100.13
+ip pim spt-switchover infinity-and-beyond
+!
+int leaf2-eth0
+ ip pim
+!
+int leaf2-eth1
+ ip pim
+!
+int leaf2-eth2
+ ip pim
+!
+int leaf2-eth3
+ ip pim
+!
diff --git a/tests/topotests/bgp_evpn_mh/leaf2/zebra.conf b/tests/topotests/bgp_evpn_mh/leaf2/zebra.conf
new file mode 100644
index 0000000..ff680cf
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/leaf2/zebra.conf
@@ -0,0 +1,24 @@
+# spine1 connection
+int leaf2-eth0
+ ip addr 192.168.51.2/24
+!
+# spine2 connection
+int leaf2-eth1
+ ip addr 192.168.61.2/24
+!
+#torm11 connection
+int leaf2-eth2
+ ip addr 192.168.5.1/24
+!
+#torm12 connection
+int leaf2-eth3
+ ip addr 192.168.6.1/24
+!
+#torm21 connection
+int leaf2-eth4
+ ip addr 192.168.7.1/24
+!
+#torm22 connection
+int leaf2-eth5
+ ip addr 192.168.8.1/24
+!
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..b9fce46
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/spine1/evpn.conf
@@ -0,0 +1,13 @@
+frr defaults datacenter
+!
+router bgp 65001
+ bgp router-id 192.168.100.13
+ no bgp ebgp-requires-policy
+ neighbor 192.168.50.2 remote-as external
+ neighbor 192.168.51.2 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.50.2 activate
+ neighbor 192.168.51.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..018d244
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/spine1/pim.conf
@@ -0,0 +1,18 @@
+ip pim ecmp
+ip pim rp 192.168.100.13
+#debug pim packets
+#debug pim packet register
+#debug pim event
+#debug pim trace
+#debug pim vxlan
+ip pim spt-switchover infinity-and-beyond
+!
+int lo
+ ip pim
+!
+int spine1-eth0
+ ip pim
+!
+int spine1-eth1
+ 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..607a5e8
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/spine1/zebra.conf
@@ -0,0 +1,11 @@
+# leaf1 connection
+int spine1-eth0
+ ip addr 192.168.50.1/24
+!
+# leaf2 connection
+int spine1-eth1
+ ip addr 192.168.51.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..1430e10
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/spine2/evpn.conf
@@ -0,0 +1,13 @@
+frr defaults datacenter
+!
+router bgp 65001
+ bgp router-id 192.168.100.14
+ no bgp ebgp-requires-policy
+ neighbor 192.168.60.2 remote-as external
+ neighbor 192.168.61.2 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.60.2 activate
+ neighbor 192.168.61.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..0ece2ff
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/spine2/pim.conf
@@ -0,0 +1,13 @@
+ip pim ecmp
+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
+!
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..c8cec14
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/spine2/zebra.conf
@@ -0,0 +1,13 @@
+# leaf1 connection
+int spine2-eth0
+ description leaf1 connection
+ ip addr 192.168.60.1/24
+!
+# leaf2 connection
+int spine2-eth1
+ description leaf2 connection
+ ip addr 192.168.61.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..ec52278
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/test_evpn_mh.py
@@ -0,0 +1,819 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_evpn_mh.py
+#
+# Copyright (c) 2020 by
+# Cumulus Networks, Inc.
+# Anuradha Karuppiah
+#
+
+"""
+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("leaf1")
+ tgen.add_router("leaf2")
+ 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 leaf1-eth0
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["spine1"])
+ switch.add_link(tgen.gears["leaf1"])
+
+ # spine1-eth1 is connected to leaf2-eth0
+ switch = tgen.add_switch("sw2")
+ switch.add_link(tgen.gears["spine1"])
+ switch.add_link(tgen.gears["leaf2"])
+
+ # spine2-eth0 is connected to leaf1-eth1
+ switch = tgen.add_switch("sw3")
+ switch.add_link(tgen.gears["spine2"])
+ switch.add_link(tgen.gears["leaf1"])
+
+ # spine2-eth1 is connected to leaf2-eth1
+ switch = tgen.add_switch("sw4")
+ switch.add_link(tgen.gears["spine2"])
+ switch.add_link(tgen.gears["leaf2"])
+
+ ################## leaf1 ##########################
+ # leaf1-eth2 is connected to torm11-eth0
+ switch = tgen.add_switch("sw5")
+ switch.add_link(tgen.gears["leaf1"])
+ switch.add_link(tgen.gears["torm11"])
+
+ # leaf1-eth3 is connected to torm12-eth0
+ switch = tgen.add_switch("sw6")
+ switch.add_link(tgen.gears["leaf1"])
+ switch.add_link(tgen.gears["torm12"])
+
+ # leaf1-eth4 is connected to torm21-eth0
+ switch = tgen.add_switch("sw7")
+ switch.add_link(tgen.gears["leaf1"])
+ switch.add_link(tgen.gears["torm21"])
+
+ # leaf1-eth5 is connected to torm22-eth0
+ switch = tgen.add_switch("sw8")
+ switch.add_link(tgen.gears["leaf1"])
+ switch.add_link(tgen.gears["torm22"])
+
+ ##################### leaf2 ########################
+ # leaf2-eth2 is connected to torm11-eth1
+ switch = tgen.add_switch("sw9")
+ switch.add_link(tgen.gears["leaf2"])
+ switch.add_link(tgen.gears["torm11"])
+
+ # leaf2-eth3 is connected to torm12-eth1
+ switch = tgen.add_switch("sw10")
+ switch.add_link(tgen.gears["leaf2"])
+ switch.add_link(tgen.gears["torm12"])
+
+ # leaf2-eth4 is connected to torm21-eth1
+ switch = tgen.add_switch("sw11")
+ switch.add_link(tgen.gears["leaf2"])
+ switch.add_link(tgen.gears["torm21"])
+
+ # leaf2-eth5 is connected to torm22-eth1
+ switch = tgen.add_switch("sw12")
+ switch.add_link(tgen.gears["leaf2"])
+ switch.add_link(tgen.gears["torm22"])
+
+ ##################### torm11 ########################
+ # torm11-eth2 is connected to hostd11-eth0
+ switch = tgen.add_switch("sw13")
+ switch.add_link(tgen.gears["torm11"])
+ switch.add_link(tgen.gears["hostd11"])
+
+ # torm11-eth3 is connected to hostd12-eth0
+ switch = tgen.add_switch("sw14")
+ switch.add_link(tgen.gears["torm11"])
+ switch.add_link(tgen.gears["hostd12"])
+
+ ##################### torm12 ########################
+ # torm12-eth2 is connected to hostd11-eth1
+ switch = tgen.add_switch("sw15")
+ switch.add_link(tgen.gears["torm12"])
+ switch.add_link(tgen.gears["hostd11"])
+
+ # torm12-eth3 is connected to hostd12-eth1
+ switch = tgen.add_switch("sw16")
+ switch.add_link(tgen.gears["torm12"])
+ switch.add_link(tgen.gears["hostd12"])
+
+ ##################### torm21 ########################
+ # torm21-eth2 is connected to hostd21-eth0
+ switch = tgen.add_switch("sw17")
+ switch.add_link(tgen.gears["torm21"])
+ switch.add_link(tgen.gears["hostd21"])
+
+ # torm21-eth3 is connected to hostd22-eth0
+ switch = tgen.add_switch("sw18")
+ switch.add_link(tgen.gears["torm21"])
+ switch.add_link(tgen.gears["hostd22"])
+
+ ##################### torm22 ########################
+ # torm22-eth2 is connected to hostd21-eth1
+ switch = tgen.add_switch("sw19")
+ switch.add_link(tgen.gears["torm22"])
+ switch.add_link(tgen.gears["hostd21"])
+
+ # torm22-eth3 is connected to hostd22-eth1
+ switch = tgen.add_switch("sw20")
+ 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", "hostd12", "hostd22"):
+ 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..a5d45da
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/torm11/pim.conf
@@ -0,0 +1,19 @@
+!
+#debug pim packet
+#debug pim packet register
+#debug pim trace
+#debug pim event
+#debug pim vxlan
+ip pim ecmp
+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..7e09ba7
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/torm12/pim.conf
@@ -0,0 +1,19 @@
+#debug pim packet
+#debug pim packet register
+#debug pim trace
+#debug pim event
+#debug pim vxlan
+!
+ip pim ecmp
+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..6996d74
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/torm21/pim.conf
@@ -0,0 +1,19 @@
+#debug pim packet
+#debug pim packet register
+#debug pim trace
+#debug pim event
+#debug pim vxlan
+!
+ip pim ecmp
+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..6256e0e
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/torm22/pim.conf
@@ -0,0 +1,19 @@
+#debug pim packet
+#debug pim packet register
+#debug pim trace
+#debug pim event
+#debug pim vxlan
+!
+ip pim ecmp
+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..1066269
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py
@@ -0,0 +1,401 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+
+"""
+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..7e4bcc8
--- /dev/null
+++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py
@@ -0,0 +1,223 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_evpn.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by 6WIND
+#
+
+"""
+ 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_macvrf_soo_topo1/P1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/bgpd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/ospfd.conf
new file mode 100644
index 0000000..2db7edb
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/P1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/zebra.conf
new file mode 100644
index 0000000..95b5da8
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/PE1/bgp.l2vpn.evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgp.l2vpn.evpn.vni.json
new file mode 100644
index 0000000..9f93635
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgp.l2vpn.evpn.vni.json
@@ -0,0 +1,19 @@
+{
+ "vni":101,
+ "type":"L2",
+ "inKernel":"True",
+ "rd":"10.10.10.10:101",
+ "originatorIp":"10.10.10.10",
+ "mcastGroup":"0.0.0.0",
+ "siteOfOrigin":"65000:0",
+ "advertiseGatewayMacip":"Disabled",
+ "advertiseSviMacIp":"Active",
+ "sviInterface":"br101",
+ "importRts":[
+ "65000:101"
+ ],
+ "exportRts":[
+ "65000:101"
+ ]
+}
+
diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgpd.conf
new file mode 100644
index 0000000..e4d20b9
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 65000
+ timers bgp 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
+ vni 101
+ rd 10.10.10.10:101
+ route-target import 65000:101
+ route-target export 65000:101
+ exit-vni
+ advertise-svi-ip
diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/evpn.vni.json
new file mode 100644
index 0000000..4bea8b3
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/evpn.vni.json
@@ -0,0 +1,17 @@
+{
+ "vni":101,
+ "type":"L2",
+ "tenantVrf":"VRF-A",
+ "vxlanInterface":"vxlan101",
+ "vtepIp":"10.10.10.10",
+ "mcastGroup":"0.0.0.0",
+ "advertiseGatewayMacip":"No",
+ "numRemoteVteps":1,
+ "remoteVteps":[
+ {
+ "ip":"10.30.30.30",
+ "flood":"HER"
+ }
+ ]
+}
+
diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/ospfd.conf
new file mode 100644
index 0000000..f1c2b42
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/PE1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/zebra.conf
new file mode 100644
index 0000000..e269947
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/PE2/bgp.l2vpn.evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgp.l2vpn.evpn.vni.json
new file mode 100644
index 0000000..63ac730
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgp.l2vpn.evpn.vni.json
@@ -0,0 +1,19 @@
+{
+ "vni":101,
+ "type":"L2",
+ "inKernel":"True",
+ "rd":"10.30.30.30:101",
+ "originatorIp":"10.30.30.30",
+ "mcastGroup":"0.0.0.0",
+ "siteOfOrigin":"65000:0",
+ "advertiseGatewayMacip":"Disabled",
+ "advertiseSviMacIp":"Active",
+ "sviInterface":"br101",
+ "importRts":[
+ "65000:101"
+ ],
+ "exportRts":[
+ "65000:101"
+ ]
+}
+
diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgpd.conf
new file mode 100644
index 0000000..9a0830d
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgpd.conf
@@ -0,0 +1,18 @@
+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
+ vni 101
+ rd 10.30.30.30:101
+ route-target import 65000:101
+ route-target export 65000:101
+ exit-vni
+ advertise-svi-ip
diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/evpn.vni.json
new file mode 100644
index 0000000..5566fff
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/evpn.vni.json
@@ -0,0 +1,16 @@
+{
+ "vni":101,
+ "type":"L2",
+ "tenantVrf":"VRF-A",
+ "vxlanInterface":"vxlan101",
+ "vtepIp":"10.30.30.30",
+ "mcastGroup":"0.0.0.0",
+ "advertiseGatewayMacip":"No",
+ "numRemoteVteps":1,
+ "remoteVteps":[
+ {
+ "ip":"10.10.10.10",
+ "flood":"HER"
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/ospfd.conf
new file mode 100644
index 0000000..065c993
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/PE2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/zebra.conf
new file mode 100644
index 0000000..9738916
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/host1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/bgpd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/ospfd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/ospfd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/zebra.conf
new file mode 100644
index 0000000..91fae9e
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/host2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/bgpd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/ospfd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/ospfd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/zebra.conf
new file mode 100644
index 0000000..df9adeb
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/test_bgp_evpn_vxlan_macvrf_soo.py b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/test_bgp_evpn_vxlan_macvrf_soo.py
new file mode 100755
index 0000000..558f737
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/test_bgp_evpn_vxlan_macvrf_soo.py
@@ -0,0 +1,839 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# test_bgp_evpn_vxlan_macvrf_soo.py
+#
+# May 10 2023, Trey Aspelund <taspelund@nvidia.com>
+#
+# Copyright (C) 2023 NVIDIA Corporation
+#
+# Test MAC-VRF Site-of-Origin feature.
+# Ensure:
+# - routes received with SoO are installed w/o "mac-vrf soo" config
+# - invalid "mac-vrf soo" config is rejected
+# - valid "mac-vrf soo" config is applied to local VNIs
+# - valid "mac-vrf soo" is set for locally originated type-2/3 routes
+# - routes received with SoO are unimported/uninstalled from L2VNI/zebra
+# - routes received with SoO are unimported/uninstalled from L3VNI/RIB
+# - routes received with SoO are still present in global EVPN loc-rib
+#
+
+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
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # 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"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ pe1 = tgen.gears["PE1"]
+ pe2 = tgen.gears["PE2"]
+ p1 = tgen.gears["P1"]
+ host1 = tgen.gears["host1"]
+ host2 = tgen.gears["host2"]
+
+ # Setup PEs with:
+ # - vrf: VRF-A
+ # - l3vni 404: vxlan404 / br404
+ # - l2vni 101: vxlan101 / br101
+
+ ## Setup VRF
+ # pe1
+ pe1.run("ip link add VRF-A type vrf table 4000")
+ pe1.run("ip link set VRF-A up")
+ # pe2
+ pe2.run("ip link add VRF-A type vrf table 4000")
+ pe2.run("ip link set VRF-A up")
+
+ ## Setup L3VNI bridge/vxlan
+ # pe1
+ pe1.run("ip link add name br404 type bridge stp_state 0")
+ pe1.run("ip link set dev br404 addr aa:bb:cc:00:11:ff")
+ pe1.run("ip link set dev br404 master VRF-A addrgenmode none")
+ pe1.run("ip link set dev br404 up")
+ pe1.run(
+ "ip link add vxlan404 type vxlan id 404 dstport 4789 local 10.10.10.10 nolearning"
+ )
+ pe1.run("ip link set dev vxlan404 master br404 addrgenmode none")
+ pe1.run("ip link set dev vxlan404 type bridge_slave neigh_suppress on learning off")
+ pe1.run("ip link set dev vxlan404 up")
+ # pe2
+ pe2.run("ip link add name br404 type bridge stp_state 0")
+ pe2.run("ip link set dev br404 addr aa:bb:cc:00:22:ff")
+ pe2.run("ip link set dev br404 master VRF-A addrgenmode none")
+ pe2.run("ip link set dev br404 up")
+ pe2.run(
+ "ip link add vxlan404 type vxlan id 404 dstport 4789 local 10.30.30.30 nolearning"
+ )
+ pe2.run("ip link set dev vxlan404 master br404 addrgenmode none")
+ pe2.run("ip link set dev vxlan404 type bridge_slave neigh_suppress on learning off")
+ pe2.run("ip link set dev vxlan404 up")
+
+ ## Setup L2VNI bridge/vxlan + L2 PE/CE link
+ # pe1
+ 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 addr aa:bb:cc:00:11:aa")
+ pe1.run("ip link set dev br101 master VRF-A")
+ 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 dev vxlan101 type bridge_slave neigh_suppress on learning off")
+ pe1.run("ip link set dev vxlan101 up")
+ pe1.run("ip link set dev PE1-eth0 master br101")
+ pe1.run("ip link set dev PE1-eth0 up")
+ # pe2
+ 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 addr aa:bb:cc:00:22:ff")
+ pe2.run("ip link set dev br101 master VRF-A")
+ 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 dev vxlan101 type bridge_slave neigh_suppress on learning off")
+ pe2.run("ip link set dev vxlan101 up")
+ pe2.run("ip link set dev PE2-eth1 master br101")
+ pe2.run("ip link set dev PE2-eth1 up")
+
+ ## Enable IPv4 Routing
+ p1.run("sysctl -w net.ipv4.ip_forward=1")
+ pe1.run("sysctl -w net.ipv4.ip_forward=1")
+ pe2.run("sysctl -w net.ipv4.ip_forward=1")
+
+ ## tell hosts to send GARP upon IPv4 addr assignment
+ host1.run("sysctl -w net.ipv4.conf.host1-eth0.arp_announce=1")
+ host2.run("sysctl -w net.ipv4.conf.host2-eth0.arp_announce=1")
+
+ ## Load FRR config on all nodes and start topo
+ 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_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ 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)
+
+ # Let's ensure that the hosts have actually tried talking to
+ # each other. Otherwise under certain startup conditions
+ # they may not actually do any l2 arp'ing and as such
+ # the bridges won't know about the hosts on their networks
+ host1 = tgen.gears["host1"]
+ host1.run("ping -c 1 10.10.1.56")
+ host2 = tgen.gears["host2"]
+ host2.run("ping -c 1 10.10.1.55")
+
+ 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)
+
+
+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 is_installed(json_paths, soo):
+ """
+ check if any path has been selected as best.
+ optionally check for matching SoO on bestpath.
+ """
+ best = False
+ soo_present = False
+ for path in json_paths:
+ path = path[0]
+ # sometimes "bestpath" is a bool, other times it's a dict
+ # either way, the key isn't present when the bool is false...
+ # so we may as well just check for the key's existence
+ best = "bestpath" in path
+ path_keys = path.keys()
+ if best:
+ if soo:
+ soo_present = soo in path["extendedCommunity"]["string"]
+ break
+ return (best and soo_present) if soo else best
+
+
+def change_soo(pe, soo, vni):
+ soo_cmd_str = "mac-vrf soo "
+ if soo:
+ soo_cmd_str += soo
+ else:
+ soo_cmd_str = "no " + soo_cmd_str
+ pe.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65000
+ address-family l2vpn evpn
+ {}
+ """.format(
+ soo_cmd_str
+ )
+ )
+ bgp_l2vni = get_bgp_l2vni_fields(pe, vni)
+ l2vni_soo = bgp_l2vni[2]
+ return l2vni_soo == soo
+
+
+def get_evpn_rt_json_str(vni, rd, oip=None, mac=None, ip=None):
+ "convert evpn route fields into a route string + global/l2vni cli syntax"
+ # type-3
+ if oip:
+ rt_str = "[3]:[0]:[32]:[{}]".format(oip)
+ global_rt_cmd = "show bgp l2vpn evpn route rd {} type 3 json".format(rd)
+ l2vni_rt_cmd = "show bgp vni {} type 3 vtep {} json".format(vni, oip)
+ # type-2
+ else:
+ rt_str = "[2]:[0]:[48]:[{}]".format(mac)
+ global_rt_cmd = "show bgp l2vpn evpn route rd {} type 2".format(rd)
+ l2vni_rt_cmd = "show bgp vni {} type 2 mac {}".format(vni, mac)
+ if ip:
+ ip_len = 128 if ":" in ip else 32
+ rt_str += ":[{}]:[{}]".format(ip_len, ip)
+ l2vni_rt_cmd = "show bgp vni {} type 2 ip {}".format(vni, ip)
+ global_rt_cmd += " json"
+ l2vni_rt_cmd += " json"
+ return [rt_str, global_rt_cmd, l2vni_rt_cmd]
+
+
+def get_evpn_rt_json(pe, vni, rd, oip=None, mac=None, ip=None):
+ "get json global/l2vni json blobs for the corresponding evpn route"
+ rt = get_evpn_rt_json_str(vni, rd, oip, mac, ip)
+ rt_str = rt.pop(0)
+ global_rt_cmd = rt.pop(0)
+ l2vni_rt_cmd = rt.pop(0)
+ logger.info(
+ "collecting global/l2vni evpn routes for pfx {} on {}".format(rt_str, pe.name)
+ )
+ global_rt_json = pe.vtysh_cmd(global_rt_cmd, isjson=True)
+ logger.info("global evpn route for pfx {} on {}".format(rt_str, pe.name))
+ logger.info(global_rt_json)
+ l2vni_rt_json = pe.vtysh_cmd(l2vni_rt_cmd, isjson=True)
+ logger.info("l2vni evpn route for pfx {} on {}".format(rt_str, pe.name))
+ logger.info(l2vni_rt_json)
+ return [rt_str, global_rt_json, l2vni_rt_json]
+
+
+def get_bgp_l2vni_fields(pe, vni):
+ bgp_vni_output = pe.vtysh_cmd(
+ "show bgp l2vpn evpn vni {} json".format(vni), isjson=True
+ )
+ rd = bgp_vni_output["rd"]
+ oip = bgp_vni_output["originatorIp"]
+ soo = bgp_vni_output["siteOfOrigin"]
+ return [rd, oip, soo]
+
+
+def rt_test(pe, vni, rd, oip, mac, ip, soo):
+ """
+ Check installation status of a given route.
+ @pe = router where bgp routes are collected from
+ @vni = l2vni
+ @rd = rd of the route
+ @oip = originator-ip, set only for type-3 route
+ @mac = nlri mac, set only for type-2
+ @ip = nlri ip, optionally set for type-2
+ @soo = MAC-VRF SoO string, set if SoO needs to be
+ on the rt to be considered installed.
+ """
+ rt = get_evpn_rt_json(pe, vni, rd, oip, mac, ip)
+ rt_str = rt.pop(0)
+ rt_global_json = rt.pop(0)
+ rt_l2vni_json = rt.pop(0)
+
+ if (
+ not rt_global_json
+ or rd not in rt_global_json
+ or rt_str not in rt_global_json[rd]
+ ):
+ global_installed = False
+ else:
+ global_json_paths = rt_global_json[rd][rt_str]["paths"]
+ global_installed = is_installed(global_json_paths, soo)
+ if not rt_l2vni_json:
+ l2vni_installed = False
+ else:
+ if not oip:
+ # json for RT2s in l2vni don't key by route string
+ l2vni_json_paths = rt_l2vni_json["paths"]
+ l2vni_installed = is_installed(l2vni_json_paths, soo)
+ elif rt_str in rt_l2vni_json and "paths" in rt_l2vni_json[rt_str]:
+ l2vni_json_paths = rt_l2vni_json[rt_str]["paths"]
+ l2vni_installed = is_installed(l2vni_json_paths, soo)
+ else:
+ l2vni_installed = False
+ return [global_installed, l2vni_installed]
+
+
+def test_macvrf_soo():
+ "Test MAC-VRF Site-of-Origin on pe1"
+ l2vni = 101
+ l3vni = 404
+ soo = "65000:0"
+
+ 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"]
+ host2 = tgen.gears["host2"]
+ pe1 = tgen.gears["PE1"]
+ pe2 = tgen.gears["PE2"]
+
+ # Collect pe2 RD/Originator-IP
+ pe2_bgp_vni = get_bgp_l2vni_fields(pe2, l2vni)
+ pe2_rd = pe2_bgp_vni[0]
+ pe2_oip = pe2_bgp_vni[1]
+ # Collect local addrs
+ h2_mac = host2.run("ip -br link show host2-eth0").split()[2]
+ h2_ip = host2.run("ip -4 -br addr show host2-eth0").split()[2].split("/")[0]
+ pe2_mac = pe2.run("ip -br link show br101").split()[2]
+ pe2_ip = pe2.run("ip -4 -br addr show br101").split()[2].split("/")[0]
+ # Route fields
+ pe2_svi_parms = [l2vni, pe2_rd, None, pe2_mac, pe2_ip]
+ pe2_imet_parms = [l2vni, pe2_rd, pe2_oip, None, None]
+ host2_mac_parms = [l2vni, pe2_rd, None, h2_mac, None]
+ host2_neigh_parms = [l2vni, pe2_rd, None, h2_mac, h2_ip]
+ # Route strings
+ pe2_svi_rt_str, _, _ = get_evpn_rt_json_str(*pe2_svi_parms)
+ pe2_imet_rt_str, _, _ = get_evpn_rt_json_str(*pe2_imet_parms)
+ host2_mac_rt_str, _, _ = get_evpn_rt_json_str(*host2_mac_parms)
+ host2_neigh_rt_str, _, _ = get_evpn_rt_json_str(*host2_neigh_parms)
+
+ ## trigger mac/arp learn
+ host1.run("ping -c1 10.10.1.1")
+ host2.run("ping -c1 10.10.1.3")
+
+ step("Test pe2/host2 routes are installed on pe1 (global/l2vni)")
+
+ # expected state:
+ # - global table: present w/o soo
+ # - l2vni table: present w/o soo
+ assertmsg = "{} missing on {} in {}{} evpn table(s)"
+ global_parms = [pe2.name, "global", ""]
+ l2vni_parms = [pe2.name, "l2vni", l2vni]
+ # pe2's type-2 for l2vni 101 svi mac/ip
+ test_f = partial(rt_test, pe2, *pe2_svi_parms, None)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(pe2_svi_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(pe2_svi_rt_str, *l2vni_parms)
+ # pe2's type-3 for l2vni 101
+ test_f = partial(rt_test, pe2, *pe2_imet_parms, None)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(pe2_imet_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(pe2_imet_rt_str, *l2vni_parms)
+ # mac-only type-2 for host2
+ test_f = partial(rt_test, pe1, *host2_mac_parms, None)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(host2_mac_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(host2_mac_rt_str, *l2vni_parms)
+ # mac+ip type-2 for host2
+ test_f = partial(rt_test, pe1, *host2_neigh_parms, None)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(host2_neigh_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(host2_neigh_rt_str, *l2vni_parms)
+
+ step("Add valid SoO config to pe2")
+ test_f = partial(change_soo, pe2, soo, l2vni)
+ _, res = topotest.run_and_expect(test_f, True, count=10, wait=1)
+ assertmsg = "soo '{}' not properly applied on {}".format(soo, pe2.name)
+ assert res == True, assertmsg
+
+ step("Test valid config applied to L2VNI on pe2")
+ ## expected state:
+ ## - global table: present w/ soo
+ ## - l2vni table: present w/ soo
+ assertmsg = "{} not originated with soo {} by {} in {}{} evpn table(s)"
+ global_parms = [soo, pe2.name, "global", ""]
+ l2vni_parms = [soo, pe2.name, "l2vni", l2vni]
+ # type-2 for l2vni 101 svi mac/ip
+ test_f = partial(rt_test, pe2, *pe2_svi_parms, soo)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(pe2_svi_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(pe2_svi_rt_str, *l2vni_parms)
+ # type-3 for l2vni 101
+ test_f = partial(rt_test, pe2, *pe2_imet_parms, soo)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(pe2_imet_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(pe2_imet_rt_str, *l2vni_parms)
+
+ step("Test invalid SoO config on pe2")
+ test_f = partial(change_soo, pe2, "1:1:1", l2vni)
+ _, res = topotest.run_and_expect(test_f, False, count=10, wait=1)
+ assertmsg = "soo '1:1:1' should not have been allowed on {}".format(pe2.name)
+ assert res == False, assertmsg
+
+ step("Test valid SoO applied to host2 routes (mac-only + mac/ip) on pe2")
+
+ ## expected state:
+ ## - global table: present w/ soo
+ ## - l2vni table: present w/ soo
+ assertmsg = "{} not originated with soo {} by {} in {}{} evpn table(s)"
+ global_parms = [soo, pe1.name, "global", ""]
+ l2vni_parms = [soo, pe1.name, "l2vni", l2vni]
+ # mac-only type-2 for host2
+ test_f = partial(rt_test, pe2, *host2_mac_parms, soo)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(host2_mac_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(host2_mac_rt_str, *l2vni_parms)
+ # mac+ip type-2 for host2
+ test_f = partial(rt_test, pe2, *host2_neigh_parms, soo)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(host2_neigh_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(host2_neigh_rt_str, *l2vni_parms)
+
+ step("Add valid SoO to pe1")
+ test_f = partial(change_soo, pe1, soo, l2vni)
+ _, res = topotest.run_and_expect(test_f, True, count=10, wait=1)
+ assertmsg = "soo '{}' not properly applied on {}".format(soo, pe1.name)
+ assert res == True, assertmsg
+
+ step("Test pe2's routes are filtered from l2vni on pe1.")
+ ## expected state:
+ ## - global table: present w/ soo
+ ## - l2vni table: not present
+ global_assertmsg = "{} with soo {} from {} missing from global evpn table"
+ l2vni_assertmsg = "{} with soo {} from {} not filtered from {}{} evpn table"
+ global_parms = [soo, pe1.name, "global", ""]
+ l2vni_parms = [soo, pe1.name, "l2vni", l2vni]
+ # pe2's svi route
+ test_f = partial(rt_test, pe1, *pe2_svi_parms, soo)
+ _, res = topotest.run_and_expect(test_f, [True, False], count=30, wait=1)
+ assert res[0] == True, global_assertmsg.format(pe2_svi_rt_str, *global_parms)
+ assert res[1] == False, l2vni_assertmsg.format(pe2_svi_rt_str, *l2vni_parms)
+ # pe2's imet route
+ test_f = partial(rt_test, pe1, *pe2_imet_parms, soo)
+ _, res = topotest.run_and_expect(test_f, [True, False], count=30, wait=1)
+ assert res[0] == True, global_assertmsg.format(pe2_imet_rt_str, *global_parms)
+ assert res[1] == False, l2vni_assertmsg.format(pe2_imet_rt_str, *l2vni_parms)
+ # mac-only type-2 for host2
+ test_f = partial(rt_test, pe1, *host2_mac_parms, soo)
+ _, res = topotest.run_and_expect(test_f, [True, False], count=30, wait=1)
+ assert res[0] == True, global_assertmsg.format(host2_mac_rt_str, *global_parms)
+ assert res[1] == False, l2vni_assertmsg.format(host2_mac_rt_str, *l2vni_parms)
+ # mac+ip type-2 for host2
+ test_f = partial(rt_test, pe1, *host2_neigh_parms, soo)
+ _, res = topotest.run_and_expect(test_f, [True, False], count=30, wait=1)
+ assert res[0] == True, global_assertmsg.format(host2_neigh_rt_str, *global_parms)
+ assert res[1] == False, l2vni_assertmsg.format(host2_neigh_rt_str, *l2vni_parms)
+
+ step("Remove SoO from pe1")
+ test_f = partial(change_soo, pe1, "", l2vni)
+ _, res = topotest.run_and_expect(test_f, True, count=10, wait=1)
+ assertmsg = "soo '{}' not properly removed from {}".format(soo, pe1.name)
+ assert res == True, assertmsg
+
+ step("Test pe2/host2 routes are installed on pe1 (global/l2vni)")
+ ## expected state:
+ ## - global table: present w/ soo
+ ## - l2vni table: present w/ soo
+ assertmsg = "{} with soo {} missing on {} in {}{} evpn table"
+ global_parms = [soo, pe1.name, "global", ""]
+ l2vni_parms = [soo, pe1.name, "l2vni", l2vni]
+ # pe2's type-2 for l2vni 101 svi mac/ip
+ test_f = partial(rt_test, pe1, *pe2_svi_parms, soo)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(pe2_svi_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(pe2_svi_rt_str, *l2vni_parms)
+ # pe2's type-3 for l2vni 101
+ test_f = partial(rt_test, pe1, *pe2_imet_parms, soo)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(pe2_imet_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(pe2_imet_rt_str, *l2vni_parms)
+ # mac-only type-2 for host2
+ test_f = partial(rt_test, pe1, *host2_mac_parms, soo)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(host2_mac_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(host2_mac_rt_str, *l2vni_parms)
+ # mac+ip type-2 for host2
+ test_f = partial(rt_test, pe1, *host2_neigh_parms, soo)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(host2_neigh_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(host2_neigh_rt_str, *l2vni_parms)
+
+ step("Remove SoO from pe2")
+ test_f = partial(change_soo, pe2, "", l2vni)
+ _, res = topotest.run_and_expect(test_f, True, count=10, wait=1)
+ assertmsg = "soo '{}' not properly removed from {}".format(soo, pe2.name)
+ assert res == True, assertmsg
+
+ step("Test pe2's 'self' routes are installed on pe1 (global/l2vni)")
+ ## expected state:
+ ## - global table: present w/o soo
+ ## - l2vni table: present w/o soo
+ assertmsg = "{} missing on {} in {}{} evpn table(s)"
+ global_parms = [pe1.name, "global", ""]
+ l2vni_parms = [pe1.name, "l2vni", l2vni]
+ # pe2's type-2 for l2vni 101 svi mac/ip
+ test_f = partial(rt_test, pe1, *pe2_svi_parms, None)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(pe2_svi_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(pe2_svi_rt_str, *l2vni_parms)
+ # pe2's type-3 for l2vni 101
+ test_f = partial(rt_test, pe1, *pe2_imet_parms, None)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(pe2_imet_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(pe2_imet_rt_str, *l2vni_parms)
+ # mac-only type-2 for host2
+ test_f = partial(rt_test, pe1, *host2_mac_parms, None)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(host2_mac_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(host2_mac_rt_str, *l2vni_parms)
+ # mac+ip type-2 for host2
+ test_f = partial(rt_test, pe1, *host2_neigh_parms, None)
+ _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1)
+ assert res[0] == True, assertmsg.format(host2_neigh_rt_str, *global_parms)
+ assert res[1] == True, assertmsg.format(host2_neigh_rt_str, *l2vni_parms)
+
+ # 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_evpn_vxlan_svd_topo1/P1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/bgpd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/ospfd.conf
new file mode 100644
index 0000000..2db7edb
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_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_svd_topo1/P1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/zebra.conf
new file mode 100644
index 0000000..95b5da8
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_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_svd_topo1/PE1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf
new file mode 100644
index 0000000..9fb2bd6
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf
@@ -0,0 +1,19 @@
+router bgp 65000
+ timers bgp 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
+!
+router bgp 65000 vrf vrf-red
+ !
+ address-family l2vpn evpn
+ route-target import *:300
+ route-target import auto
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/evpn.vni.json
new file mode 100644
index 0000000..98ae92c
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/evpn.vni.json
@@ -0,0 +1,16 @@
+{
+ "vni":101,
+ "type":"L2",
+ "vrf":"default",
+ "vxlanInterface":"vxlan0",
+ "vtepIp":"10.10.10.10",
+ "mcastGroup":"0.0.0.0",
+ "advertiseGatewayMacip":"No",
+ "remoteVteps":[
+ {
+ "ip":"10.30.30.30",
+ "flood":"HER"
+ }
+ ]
+}
+
diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/ospfd.conf
new file mode 100644
index 0000000..f1c2b42
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_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_svd_topo1/PE1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf
new file mode 100644
index 0000000..8c6cf3e
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf
@@ -0,0 +1,13 @@
+!
+log file zebra.log
+!
+vrf vrf-red
+ vni 100
+ exit-vrf
+!
+!
+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_svd_topo1/PE2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/bgpd.conf
new file mode 100644
index 0000000..10809da
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/bgpd.conf
@@ -0,0 +1,24 @@
+!
+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
+!
+router bgp 65000 vrf vrf-blue
+ !
+ address-family ipv4 unicast
+ redistribute static
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ advertise ipv4 unicast
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/evpn.vni.json
new file mode 100644
index 0000000..5c05978
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/evpn.vni.json
@@ -0,0 +1,15 @@
+{
+ "vni":101,
+ "type":"L2",
+ "vrf":"default",
+ "vxlanInterface":"vxlan0",
+ "vtepIp":"10.30.30.30",
+ "mcastGroup":"0.0.0.0",
+ "advertiseGatewayMacip":"No",
+ "remoteVteps":[
+ {
+ "ip":"10.10.10.10",
+ "flood":"HER"
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/ospfd.conf
new file mode 100644
index 0000000..065c993
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_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_svd_topo1/PE2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/zebra.conf
new file mode 100644
index 0000000..cee4355
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/zebra.conf
@@ -0,0 +1,17 @@
+vrf vrf-blue
+ vni 300
+ exit-vrf
+!
+vrf vrf-red
+ vni 100
+ exit-vrf
+!
+interface lo
+ ip address 10.30.30.30/32
+interface PE2-eth0
+ ip address 10.20.2.3/24
+!
+interface vrf-blue
+ ip address 30.0.0.3/24
+!
+ip route 4.4.4.1/32 30.0.0.100 vrf vrf-blue
diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/__init__.py b/tests/topotests/bgp_evpn_vxlan_svd_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/__init__.py
diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/bgpd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/ospfd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/ospfd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/zebra.conf
new file mode 100644
index 0000000..91fae9e
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_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_svd_topo1/host2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/bgpd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/ospfd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/ospfd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/zebra.conf
new file mode 100644
index 0000000..df9adeb
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_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_svd_topo1/test_bgp_evpn_vxlan_svd.py b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py
new file mode 100755
index 0000000..65c0c35
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py
@@ -0,0 +1,556 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_evpn_vxlan_svd.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 NVIDIA Corporation
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee 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 and route signalling over BGP
+using Single Vxlan Device Configurtion
+"""
+
+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
+from lib.common_config import required_linux_kernel_version
+
+# 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_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf):
+ pe = tgen.gears[pe_name]
+
+ # configure vlan aware bridge
+ pe.run("ip link add name bridge type bridge stp_state 0")
+ pe.run("ip link set dev bridge type bridge vlan_filtering 1")
+ pe.run("bridge vlan add vid 1 dev bridge self")
+ pe.run("ip link set dev bridge up")
+
+ # setup svi
+ pe.run("ip link add link bridge name vlan1 type vlan id 1 protocol 802.1q")
+ pe.run("ip link set dev vlan1 up")
+ pe.run("ip addr add {0} dev vlan1".format(svi_ip))
+ pe.run("/sbin/sysctl net.ipv4.conf.vlan1.arp_accept=1")
+
+ # setup single vxlan device
+ pe.run(
+ "ip link add dev vxlan0 type vxlan dstport 4789 local {0} nolearning external".format(
+ tunnel_local_ip
+ )
+ )
+ pe.run("ip link set dev vxlan0 master bridge")
+ pe.run("bridge link set dev vxlan0 vlan_tunnel on")
+ pe.run("bridge link set dev vxlan0 neigh_suppress on")
+ pe.run("bridge link set dev vxlan0 learning off")
+ pe.run("bridge vlan add dev vxlan0 vid 1")
+ pe.run("bridge vlan add dev vxlan0 vid 1 tunnel_info id 101")
+ pe.run("ip link set up dev vxlan0")
+
+ # setup PE interface
+ pe.run("ip link set dev {0}-{1} master bridge".format(pe_name, intf))
+ pe.run("bridge vlan del vid 1 dev {0}-{1}".format(pe_name, intf))
+ pe.run("bridge vlan del vid 1 untagged pvid dev {0}-{1}".format(pe_name, intf))
+ pe.run("bridge vlan add vid 1 dev {0}-{1}".format(pe_name, intf))
+ pe.run("bridge vlan add vid 1 pvid untagged dev {0}-{1}".format(pe_name, intf))
+
+ # l3vni 100
+ pe.run("ip link add vrf-red type vrf table 1400")
+ pe.run("ip link add link bridge name vlan100 type vlan id 100 protocol 802.1q")
+ pe.run("ip link set dev vlan100 master vrf-blue")
+ pe.run("ip link set dev vlan100 up")
+ pe.run("bridge vlan add vid 100 dev bridge self")
+ pe.run("bridge vlan add dev vxlan0 vid 100")
+ pe.run("bridge vlan add dev vxlan0 vid 100 tunnel_info id 100")
+
+ # add a vrf for testing DVNI
+ if pe_name == "PE2":
+ pe.run("ip link add vrf-blue type vrf table 2400")
+ pe.run("ip link add link bridge name vlan300 type vlan id 300 protocol 802.1q")
+ pe.run("ip link set dev vlan300 master vrf-blue")
+ pe.run("ip link set dev vlan300 up")
+ pe.run("bridge vlan add vid 300 dev bridge self")
+ pe.run("bridge vlan add dev vxlan0 vid 300")
+ pe.run("bridge vlan add dev vxlan0 vid 300 tunnel_info id 300")
+
+
+def setup_p_router(tgen, p_name):
+ p1 = tgen.gears[p_name]
+ p1.run("sysctl -w net.ipv4.ip_forward=1")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ result = required_linux_kernel_version("5.7")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met, kernel version should be >= 5.7")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ setup_pe_router(tgen, "PE1", "10.10.10.10", "10.10.1.1/24", "eth0")
+ setup_pe_router(tgen, "PE2", "10.30.30.30", "10.10.1.3/24", "eth1")
+ setup_p_router(tgen, "P1")
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registred 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()
+
+ # tgen.mininet_cli()
+ # 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 check_flood_entry_present(pe, vni, vtep):
+ if not topotest.iproute2_is_fdb_get_capable():
+ return None
+
+ output = pe.run(
+ "bridge fdb get 00:00:00:00:00:00 dev vxlan0 vni {} self".format(vni)
+ )
+
+ if str(vtep) not in output:
+ return output
+
+ 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)
+
+ # Let's ensure that the hosts have actually tried talking to
+ # each other. Otherwise under certain startup conditions
+ # they may not actually do any l2 arp'ing and as such
+ # the bridges won't know about the hosts on their networks
+ host1 = tgen.gears["host1"]
+ host1.run("ping -c 1 10.10.1.56")
+ host2 = tgen.gears["host2"]
+ host2.run("ping -c 1 10.10.1.55")
+
+ 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)
+
+ vtep = "10.30.30.30"
+ test_func = partial(check_flood_entry_present, pe1, 101, vtep)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe1.name, vtep)
+ assert result is None, assertmsg
+
+
+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)
+
+ vtep = "10.10.10.10"
+ test_func = partial(check_flood_entry_present, pe2, 101, vtep)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe2.name, vtep)
+ assert result is None, assertmsg
+
+
+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:
+ if vni not in remote_output_json:
+ continue
+
+ 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)
+
+
+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 show_dvni_route(pe, vni, prefix, vrf):
+ output = pe.vtysh_cmd("show ip route vrf {} {}".format(vrf, prefix))
+
+ if str(vni) not in output:
+ return output
+
+ output = pe.run("ip route show vrf {} {}".format(vrf, prefix))
+
+ if str(vni) not in output:
+ return output
+
+ return None
+
+
+def test_dvni():
+ "test Downstream VNI works as expected importing into PE1"
+
+ 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"]
+
+ prefix = "4.4.4.1/32"
+ test_func = partial(show_dvni_route, pe1, 300, prefix, "vrf-red")
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"{}" DVNI route {} not found'.format(pe1.name, prefix)
+ 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/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..dbbfc82
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/bgpd.conf
@@ -0,0 +1,11 @@
+router bgp 65000
+ timers bgp 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..eb8668b
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/evpn.vni.json
@@ -0,0 +1,17 @@
+{
+ "vni":101,
+ "type":"L2",
+ "tenantVrf":"default",
+ "vxlanInterface":"vxlan101",
+ "vtepIp":"10.10.10.10",
+ "mcastGroup":"0.0.0.0",
+ "advertiseGatewayMacip":"No",
+ "numRemoteVteps":1,
+ "remoteVteps":[
+ {
+ "ip":"10.30.30.30",
+ "flood":"HER"
+ }
+ ]
+}
+
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..befb416
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/evpn.vni.json
@@ -0,0 +1,16 @@
+{
+ "vni":101,
+ "type":"L2",
+ "tenantVrf":"default",
+ "vxlanInterface":"vxlan101",
+ "vtepIp":"10.30.30.30",
+ "mcastGroup":"0.0.0.0",
+ "advertiseGatewayMacip":"No",
+ "numRemoteVteps":1,
+ "remoteVteps":[
+ {
+ "ip":"10.10.10.10",
+ "flood":"HER"
+ }
+ ]
+}
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..2884043
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py
@@ -0,0 +1,436 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_evpn_vxlan.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+"""
+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)
+
+ # Let's ensure that the hosts have actually tried talking to
+ # each other. Otherwise under certain startup conditions
+ # they may not actually do any l2 arp'ing and as such
+ # the bridges won't know about the hosts on their networks
+ host1 = tgen.gears["host1"]
+ host1.run("ping -c 1 10.10.1.56")
+ host2 = tgen.gears["host2"]
+ host2.run("ping -c 1 10.10.1.55")
+
+ 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_extcomm_list_delete/__init__.py b/tests/topotests/bgp_extcomm_list_delete/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_extcomm_list_delete/__init__.py
diff --git a/tests/topotests/bgp_extcomm_list_delete/r1/bgpd.conf b/tests/topotests/bgp_extcomm_list_delete/r1/bgpd.conf
new file mode 100644
index 0000000..3394c1c
--- /dev/null
+++ b/tests/topotests/bgp_extcomm_list_delete/r1/bgpd.conf
@@ -0,0 +1,20 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.255.2 remote-as 65001
+ address-family ipv4 unicast
+ network 10.10.10.1/32 route-map r2-out-rt
+ network 10.10.10.2/32 route-map r2-out-soo
+ network 10.10.10.3/32 route-map r2-out-nt
+ redistribute connected
+ exit-address-family
+!
+route-map r2-out-rt permit 10
+ set extcommunity rt 1.1.1.1:1 2.2.2.2:2 3.3.3.3:3 4.4.4.4:4
+!
+route-map r2-out-soo permit 20
+ set extcommunity soo 1.1.1.1:1 2.2.2.2:2 3.3.3.3:3 4.4.4.4:4
+!
+route-map r2-out-nt permit 30
+ set extcommunity nt 192.168.255.2:0 2.2.2.2:0 3.3.3.3:0 4.4.4.4:0
+!
diff --git a/tests/topotests/bgp_extcomm_list_delete/r1/zebra.conf b/tests/topotests/bgp_extcomm_list_delete/r1/zebra.conf
new file mode 100644
index 0000000..e2c399e
--- /dev/null
+++ b/tests/topotests/bgp_extcomm_list_delete/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_extcomm_list_delete/r2/bgpd.conf b/tests/topotests/bgp_extcomm_list_delete/r2/bgpd.conf
new file mode 100644
index 0000000..ca497e6
--- /dev/null
+++ b/tests/topotests/bgp_extcomm_list_delete/r2/bgpd.conf
@@ -0,0 +1,10 @@
+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
+!
+route-map r1-in permit 10
+!
diff --git a/tests/topotests/bgp_extcomm_list_delete/r2/zebra.conf b/tests/topotests/bgp_extcomm_list_delete/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_extcomm_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_extcomm_list_delete/test_bgp_extcomm-list_delete.py b/tests/topotests/bgp_extcomm_list_delete/test_bgp_extcomm-list_delete.py
new file mode 100644
index 0000000..eb05986
--- /dev/null
+++ b/tests/topotests/bgp_extcomm_list_delete/test_bgp_extcomm-list_delete.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright 2023 6WIND S.A.
+# Authored by Farid Mihoub <farid.mihoub@6wind.com>
+#
+
+"""
+bgp_extcomm_list-delete.py:
+
+Test the following commands:
+route-map test permit 10
+ set extended-comm-list <arg> delete
+"""
+
+import functools
+import json
+import os
+import pytest
+import re
+import sys
+
+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
+from lib import topotest
+
+
+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_convergence():
+ tgen = get_topogen()
+ r2 = tgen.gears["r2"]
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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": 4,
+ }
+ },
+ }
+ }
+ 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 initially"
+
+
+def _set_extcomm_list(gear, ecom_t, ecom):
+ "Set the extended community for deletion."
+ cmd = [
+ "con t\n",
+ f"bgp extcommunity-list standard r1-{ecom_t} permit {ecom_t} {ecom}\n",
+ f"route-map r1-in permit 10\n",
+ f"set extended-comm-list r1-{ecom_t} delete\n",
+ ]
+ gear.vtysh_cmd("".join(cmd))
+
+
+def _bgp_extcomm_list_del_check(gear, prefix, ecom):
+ """
+ Check the non-presense of the extended community for the given prefix.
+ """
+ # get the extended community list attribute for the given prefix
+ output = json.loads(gear.vtysh_cmd(f"show ip bgp {prefix} json"))
+ ecoms = output.get("paths", [])[0].get("extendedCommunity", {})
+ ecoms = ecoms.get("string")
+
+ # ecoms might be None at the first time
+ if not ecoms:
+ return False
+ return re.search(ecom, ecoms) is None
+
+
+def test_rt_extcomm_list_delete():
+ tgen = get_topogen()
+ r2 = tgen.gears["r2"]
+
+ # set the extended community for deletion
+ _set_extcomm_list(r2, "rt", "1.1.1.1:1")
+
+ # check for the deletion of the extended community
+ test_func = functools.partial(
+ _bgp_extcomm_list_del_check, r2, "10.10.10.1/32", r"1.1.1.1:1")
+ _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5)
+ assert result, "RT extended community 1.1.1.1:1 was not stripped."
+
+
+def test_soo_extcomm_list_delete():
+ tgen = get_topogen()
+ r2 = tgen.gears["r2"]
+
+ # set the extended community for deletion
+ _set_extcomm_list(r2, "soo", "2.2.2.2:2")
+
+ # check for the deletion of the extended community
+ test_func = functools.partial(
+ _bgp_extcomm_list_del_check, r2, "10.10.10.2/32", r"2.2.2.2:2")
+ _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5)
+ assert result, "SoO extended community 2.2.2.2:2 was not stripped."
+
+
+def test_nt_extcomm_list_delete():
+ tgen = get_topogen()
+ r2 = tgen.gears["r2"]
+
+ # set the extended community for deletion
+ _set_extcomm_list(r2, "nt", "3.3.3.3:0")
+
+ # check for the deletion of the extended community
+ test_func = functools.partial(
+ _bgp_extcomm_list_del_check, r2, "10.10.10.3/32", r"3.3.3.3")
+ _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5)
+ assert result, "NT extended community 3.3.3.3:0 was not stripped."
+
+
+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..eef122b
--- /dev/null
+++ b/tests/topotests/bgp_extended_optional_parameters_length/test_bgp_extended_optional_parameters_length.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..caf700d
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/ospf_neighbor.json
@@ -0,0 +1,16 @@
+{
+ "neighbors":{
+ "192.168.0.2":[
+ {
+ "nbrPriority":5,
+ "converged":"Full"
+ }
+ ],
+ "192.168.0.3":[
+ {
+ "nbrPriority":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..a4e51fd
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/zebra.conf
@@ -0,0 +1,29 @@
+!
+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
+!
+no ip nht resolve-via-default
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..3a168ba
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/ospf_neighbor.json
@@ -0,0 +1,16 @@
+{
+ "neighbors":{
+ "192.168.0.1":[
+ {
+ "nbrPriority":10,
+ "converged":"Full"
+ }
+ ],
+ "192.168.0.3":[
+ {
+ "nbrPriority":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..9f8c059
--- /dev/null
+++ b/tests/topotests/bgp_features/r3/ospf_neighbor.json
@@ -0,0 +1,16 @@
+{
+ "neighbors":{
+ "192.168.0.1":[
+ {
+ "nbrPriority":10,
+ "converged":"Full"
+ }
+ ],
+ "192.168.0.2":[
+ {
+ "nbrPriority":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..43f4905
--- /dev/null
+++ b/tests/topotests/bgp_features/test_bgp_features.py
@@ -0,0 +1,1102 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_features.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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 get_shut_msg_count(tgen):
+ shuts = {}
+ for rtrNum in [2, 4]:
+ shutmsg = tgen.net["r{}".format(rtrNum)].cmd_nostatus(
+ 'grep -c "NOTIFICATION.*Cease/Administrative Shutdown" bgpd.log', warn=False
+ )
+ try:
+ shuts[rtrNum] = int(shutmsg.strip())
+ except ValueError:
+ shuts[rtrNum] = 0
+ return shuts
+
+
+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)
+
+ shuts_before = get_shut_msg_count(tgen)
+
+ 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
+
+ shuts_after = get_shut_msg_count(tgen)
+
+ for k in shuts_before:
+ assert shuts_before[k] + 1 == shuts_after[k]
+
+
+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(
+ 'grep -e "NOTIFICATION.*Cease/Administrative Shutdown.*ABCDabcd" bgpd.log'
+ )
+ assertmsg = "BGP shutdown message not received on router R{}".format(rtrNum)
+ assert shut_message != "", assertmsg
+
+
+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=5, 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=5, 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=5, 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=5, 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=5, 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=5, 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..383a95b
--- /dev/null
+++ b/tests/topotests/bgp_flowspec/peer1/exabgp.cfg
@@ -0,0 +1,33 @@
+neighbor 10.0.1.1 {
+router-id 10.0.1.101;
+hold-time 10;
+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..a2be859
--- /dev/null
+++ b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_flowspec_topo.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by 6WIND
+#
+
+"""
+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
+from lib.common_config import generate_support_bundle
+
+# 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=210, wait=1)
+ assertmsg = "BGP router network did not converge"
+ if res is not None:
+ generate_support_bundle()
+ assert res is None, assertmsg
+ generate_support_bundle()
+
+
+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..5e2b2f3
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-1.py
@@ -0,0 +1,1527 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+
+"""
+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, kernel version should be >=4.16")
+
+ 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: R-bit should not be set to True in r2\n"
+ "Found: {}".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 "
+ "Expected: R-bit should not be set to True in r2\n"
+ "Found: {}".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..13c5ba5
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py
@@ -0,0 +1,391 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+
+"""
+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, kernel version should be >=4.16")
+
+ 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..1a8f830
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py
@@ -0,0 +1,2720 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+
+"""
+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, kernel version should be >=4.16")
+
+ 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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[Helper 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] : R1 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}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ logger.info("[Phase 4] : R1 is UP and GR state is correct ")
+
+ 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: F-bit should not be set to True in r1\n"
+ "Found: {}".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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: R-bit should not be set to True in r1\n"
+ "Found: {}".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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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
+ )
+
+ # restart the daemon or we get warnings in the follow-on tests
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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)
+
+ # restart the daemon or we get warnings in the follow-on tests
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ 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)
+
+ # restart the daemon or we get warnings in the follow-on tests
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ 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..31aaa0b
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py
@@ -0,0 +1,1685 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+
+"""
+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, kernel version should be >=4.16")
+
+ 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
+ )
+
+ # restart the daemon or we get warnings in the follow-on tests
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ 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
+ )
+
+ # restart the daemon or we get warnings in the follow-on tests
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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
+ )
+
+ # restart the daemon or we get warnings in the follow-on tests
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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..cb6bf56
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-1.py
@@ -0,0 +1,1497 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+
+"""
+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, kernel version should be >=4.16")
+
+ 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 "
+ "Expected: EOR should not be set to True in r2\n"
+ "Found: {}".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 "
+ "Expected: EOR should not be set to True in r1\n"
+ "Found: {}".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 "
+ "Expected: EOR should not be set to True in r3\n"
+ "Found: {}".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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r2"
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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..cf9a474
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py
@@ -0,0 +1,1194 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+
+"""
+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, kernel version should be >=4.16")
+
+ 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: F-bit should not be set to True in r3\n"
+ "Found: {}".format(tc_name, 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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..4746d71
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py
@@ -0,0 +1,1346 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+
+"""
+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, kernel version should be >=4.16")
+
+ 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 "
+ "Expected: F-bit should not be set to True in r3\n"
+ "Found: {}".format(tc_name, 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: EOR should not be set to True in r5\n"
+ "Found: {}".format(tc_name, 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: EOR should not be set to True in r3\n"
+ "Found: {}".format(tc_name, 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 "
+ "Expected: EOR should not be set to True in r1\n"
+ "Found: {}".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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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..1c41df9
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py
@@ -0,0 +1,1009 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+
+"""
+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, kernel version should be >=4.16")
+
+ 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 "
+ "Expected: EOR should not be set to True in r2\n"
+ "Found: {}".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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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/test_bgp_gr_functionality_topo3.py b/tests/topotests/bgp_gr_functionality_topo3/test_bgp_gr_functionality_topo3.py
new file mode 100644
index 0000000..593a8d6
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo3/test_bgp_gr_functionality_topo3.py
@@ -0,0 +1,541 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+
+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, kernel version should be >=4.16")
+
+ 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..2ffcb72
--- /dev/null
+++ b/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# bgp_gr_notification.py
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+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..758d797
--- /dev/null
+++ b/tests/topotests/bgp_gr_restart_retain_routes/r2/zebra.conf
@@ -0,0 +1,5 @@
+no zebra nexthop kernel enable
+!
+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..a820b4b
--- /dev/null
+++ b/tests/topotests/bgp_gr_restart_retain_routes/test_bgp_gr_restart_retain_routes.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+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 = json.loads(r2.cmd("ip -j route show 172.16.255.1/32 proto bgp dev r2-eth0"))
+ expected = [{"dst":"172.16.255.1","gateway":"192.168.255.1","metric":20}]
+ 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("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() is None, "Failed to retain BGP routes in kernel on R2"
+
+
+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..61a0fe6
--- /dev/null
+++ b/tests/topotests/bgp_gshut/test_bgp_gshut.py
@@ -0,0 +1,343 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_gshut.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Vivek Venkatraman <vivek@nvidia.com>
+#
+
+"""
+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..5ec9db0
--- /dev/null
+++ b/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py
@@ -0,0 +1,593 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""
+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, kernel version should be >=4.16")
+
+ 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..1e0f726
--- /dev/null
+++ b/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py
@@ -0,0 +1,640 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""
+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, kernel version should be >=4.16")
+
+ 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..ddb5fe5
--- /dev/null
+++ b/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+
+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..c7cb213
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_class_e_peer/test_bgp_ipv4_class_e_peer.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# bgp_ipv4_class_e_peer.py
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+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..1873fb0
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py
@@ -0,0 +1,950 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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..47cc378
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py
@@ -0,0 +1,617 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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..395ef2d
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py
@@ -0,0 +1,766 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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..56152b9
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py
@@ -0,0 +1,975 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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..526b267
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py
@@ -0,0 +1,311 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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_ll_peering/__init__.py b/tests/topotests/bgp_ipv6_ll_peering/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_ll_peering/__init__.py
diff --git a/tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf b/tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf
new file mode 100644
index 0000000..724cbf8
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65001
+ bgp router-id 10.0.0.1
+ no bgp ebgp-requires-policy
+ neighbor fe80:1::2 remote-as external
+ neighbor fe80:1::2 timers 3 10
+ neighbor fe80:1::2 interface r1-eth0
diff --git a/tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf b/tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf
new file mode 100644
index 0000000..4e93d4f
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r1-eth0
+ ipv6 address fe80:1::1/64
+!
diff --git a/tests/topotests/bgp_ipv6_ll_peering/r2/bgpd.conf b/tests/topotests/bgp_ipv6_ll_peering/r2/bgpd.conf
new file mode 100644
index 0000000..44f79df
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_ll_peering/r2/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65002
+ bgp router-id 10.0.0.2
+ no bgp ebgp-requires-policy
+ neighbor fe80:1::1 remote-as external
+ neighbor fe80:1::1 timers 3 10
+ neighbor fe80:1::1 interface r2-eth0
diff --git a/tests/topotests/bgp_ipv6_ll_peering/r2/zebra.conf b/tests/topotests/bgp_ipv6_ll_peering/r2/zebra.conf
new file mode 100644
index 0000000..1e703cd
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_ll_peering/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r2-eth0
+ ipv6 address fe80:1::2/64
+!
diff --git a/tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py b/tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py
new file mode 100644
index 0000000..ea974b5
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Check if IPv6 Link-Local BGP peering works fine.
+"""
+
+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_ipv6_link_local_peering():
+ 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": {
+ "peers": {
+ "fe80:1::2": {
+ "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, "Failed to see BGP convergence on R2"
+
+
+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..045ac91
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_ipv6_rtadv.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by 6WIND
+#
+
+"""
+ 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..41114b6
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/customize.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+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..7930f09
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/test_bgp_l3vpn_to_bgp_direct.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+
+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..0ac5350
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py
@@ -0,0 +1,216 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+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..72211fe
--- /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
+
+#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..edb3b69
--- /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
+
+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..ed76ed3
--- /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 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..1e2758c
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py
@@ -0,0 +1,878 @@
+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"',
+ "4 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"',
+ "4 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": False},
+ {"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": True},
+ {"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"',
+ "4 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"',
+ "4 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": False},
+ {"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": True},
+ {"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": False},
+ {"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": True},
+ {"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": False},
+ {"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": True},
+ {"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 13",
+ "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.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"',
+ "1 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 .First path received"
+ + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:11",
+ "pass",
+ "Route 2 details",
+)
+luCommand(
+ "ce2",
+ 'vtysh -c "show bgp ipv4 uni 6.0.2.0"',
+ "1 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 .First path received"
+ + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:12",
+ "pass",
+ "Route 2 details",
+)
+luCommand(
+ "ce3",
+ 'vtysh -c "show bgp ipv4 uni 6.0.2.0"',
+ "1 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 .First path received"
+ + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:13",
+ "pass",
+ "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:13",
+ "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..46993c7
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py
@@ -0,0 +1,253 @@
+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),
+ )
+ luCommand(
+ "ce1",
+ 'vtysh -c "show ip route summ" | grep "sharp" | cut -d " " -f 33',
+ str(num),
+ "wait",
+ "See all sharp routes in rib on ce1",
+ wait,
+ wait_time=10,
+ )
+ luCommand(
+ "ce2",
+ 'vtysh -c "show ip route summ" | grep "sharp" | cut -d " " -f 33',
+ str(num),
+ "wait",
+ "See all sharp routes in rib on ce2",
+ wait,
+ wait_time=10,
+ )
+
+ 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..60d959f
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+
+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_labeled_unicast_addpath/__init__.py b/tests/topotests/bgp_labeled_unicast_addpath/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_addpath/__init__.py
diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r1/bgpd.conf b/tests/topotests/bgp_labeled_unicast_addpath/r1/bgpd.conf
new file mode 100644
index 0000000..ae3812a
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_addpath/r1/bgpd.conf
@@ -0,0 +1,14 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 192.168.31.3 remote-as external
+ neighbor 192.168.31.3 timers 1 3
+ neighbor 192.168.31.3 timers connect 1
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 192.168.31.3 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r1/zebra.conf b/tests/topotests/bgp_labeled_unicast_addpath/r1/zebra.conf
new file mode 100644
index 0000000..fbaccda
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_addpath/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface lo
+ ip address 10.0.0.1/32
+!
+interface r1-eth0
+ ip address 192.168.31.1/24
+!
diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r2/bgpd.conf b/tests/topotests/bgp_labeled_unicast_addpath/r2/bgpd.conf
new file mode 100644
index 0000000..16dc1f3
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_addpath/r2/bgpd.conf
@@ -0,0 +1,14 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 192.168.32.3 remote-as external
+ neighbor 192.168.32.3 timers 1 3
+ neighbor 192.168.32.3 timers connect 1
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 192.168.32.3 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r2/zebra.conf b/tests/topotests/bgp_labeled_unicast_addpath/r2/zebra.conf
new file mode 100644
index 0000000..8118d56
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_addpath/r2/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface lo
+ ip address 10.0.0.1/32
+!
+interface r2-eth0
+ ip address 192.168.32.2/24
+!
diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r3/bgpd.conf b/tests/topotests/bgp_labeled_unicast_addpath/r3/bgpd.conf
new file mode 100644
index 0000000..6dd8f7f
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_addpath/r3/bgpd.conf
@@ -0,0 +1,35 @@
+!
+router bgp 65003
+ no bgp default ipv4-unicast
+ no bgp ebgp-requires-policy
+ no bgp suppress-duplicates
+ bgp bestpath as-path multipath-relax
+ neighbor 192.168.31.1 remote-as external
+ neighbor 192.168.31.1 timers 1 3
+ neighbor 192.168.31.1 timers connect 1
+ neighbor 192.168.32.2 remote-as external
+ neighbor 192.168.32.2 timers 1 3
+ neighbor 192.168.32.2 timers connect 1
+ neighbor 192.168.34.4 remote-as external
+ neighbor 192.168.34.4 timers 1 3
+ neighbor 192.168.34.4 timers connect 1
+ neighbor 192.168.35.5 remote-as external
+ neighbor 192.168.35.5 timers 1 3
+ neighbor 192.168.35.5 timers connect 1
+ neighbor 192.168.35.5 shutdown
+ address-family ipv4 labeled-unicast
+ neighbor 192.168.31.1 activate
+ neighbor 192.168.32.2 activate
+ neighbor 192.168.35.5 activate
+ neighbor 192.168.34.4 activate
+ neighbor 192.168.34.4 route-map r4 out
+ neighbor 192.168.34.4 addpath-tx-all-paths
+ exit-address-family
+ !
+!
+ip prefix-list r4 seq 5 permit 10.0.0.1/32
+!
+route-map r4 permit 10
+ match ip address prefix-list r4
+exit
+!
diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r3/zebra.conf b/tests/topotests/bgp_labeled_unicast_addpath/r3/zebra.conf
new file mode 100644
index 0000000..838413a
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_addpath/r3/zebra.conf
@@ -0,0 +1,13 @@
+!
+interface r3-eth0
+ ip address 192.168.31.3/24
+!
+interface r3-eth1
+ ip address 192.168.32.3/24
+!
+interface r3-eth2
+ ip address 192.168.34.3/24
+!
+interface r3-eth3
+ ip address 192.168.35.3/24
+!
diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r4/bgpd.conf b/tests/topotests/bgp_labeled_unicast_addpath/r4/bgpd.conf
new file mode 100644
index 0000000..e137156
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_addpath/r4/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 65004
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 192.168.34.3 remote-as external
+ neighbor 192.168.34.3 timers 1 3
+ neighbor 192.168.34.3 timers connect 1
+ address-family ipv4 labeled-unicast
+ neighbor 192.168.34.3 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r4/zebra.conf b/tests/topotests/bgp_labeled_unicast_addpath/r4/zebra.conf
new file mode 100644
index 0000000..2ec426a
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_addpath/r4/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r4-eth0
+ ip address 192.168.34.4/24
+!
diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r5/bgpd.conf b/tests/topotests/bgp_labeled_unicast_addpath/r5/bgpd.conf
new file mode 100644
index 0000000..5b38b5a
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_addpath/r5/bgpd.conf
@@ -0,0 +1,14 @@
+router bgp 65005
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 192.168.35.3 remote-as external
+ neighbor 192.168.35.3 timers 1 3
+ neighbor 192.168.35.3 timers connect 1
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 192.168.35.3 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_labeled_unicast_addpath/r5/zebra.conf b/tests/topotests/bgp_labeled_unicast_addpath/r5/zebra.conf
new file mode 100644
index 0000000..6289e4f
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_addpath/r5/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface lo
+ ip address 10.0.0.1/32
+!
+interface r5-eth0
+ ip address 192.168.35.5/24
+!
diff --git a/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py b/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py
new file mode 100644
index 0000000..f4bb487
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Check if labeled-unicast works correctly with addpath capability.
+Initially R3 MUST announce 10.0.0.1/32 multipath(2) from R1 + R2.
+Later, we enable R5 and 10.0.0.1/32 multipath(3) MUST be announced,
+R1 + R2 + R5.
+"""
+
+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, 6):
+ 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["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r3"])
+ 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_addpath_labeled_unicast():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r3 = tgen.gears["r3"]
+ r4 = tgen.gears["r4"]
+
+ def _bgp_check_received_routes(pfxcount):
+ output = json.loads(r4.vtysh_cmd("show bgp ipv4 labeled-unicast summary json"))
+ expected = {
+ "peers": {
+ "192.168.34.3": {
+ "pfxRcd": pfxcount,
+ "state": "Established",
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_received_routes, 2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert (
+ result is None
+ ), "Failed to receive labeled-unicast with addpath (multipath=2)"
+
+ step("Enable BGP session for R5")
+ r3.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65003
+ no neighbor 192.168.35.5 shutdown
+ """
+ )
+
+ test_func = functools.partial(_bgp_check_received_routes, 3)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert (
+ result is None
+ ), "Failed to receive labeled-unicast with addpath (multipath=3)"
+
+ step("Disable BGP session for R5")
+ r3.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65003
+ neighbor 192.168.35.5 shutdown
+ """
+ )
+
+ test_func = functools.partial(_bgp_check_received_routes, 2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert (
+ result is None
+ ), "Failed to receive labeled-unicast with addpath (multipath=2)"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_labeled_unicast_default_originate/__init__.py b/tests/topotests/bgp_labeled_unicast_default_originate/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_default_originate/__init__.py
diff --git a/tests/topotests/bgp_labeled_unicast_default_originate/r1/bgpd.conf b/tests/topotests/bgp_labeled_unicast_default_originate/r1/bgpd.conf
new file mode 100644
index 0000000..d0d1390
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_default_originate/r1/bgpd.conf
@@ -0,0 +1,35 @@
+!
+router bgp 65001
+ no bgp default ipv4-unicast
+ no bgp default ipv6-unicast
+ no bgp ebgp-requires-policy
+ neighbor 192.168.12.2 remote-as external
+ neighbor 192.168.12.2 timers 1 3
+ neighbor 192.168.12.2 timers connect 1
+ neighbor 2001:db8:12::2 remote-as external
+ neighbor 2001:db8:12::2 timers 1 3
+ neighbor 2001:db8:12::2 timers connect 1
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 192.168.12.2 activate
+ neighbor 192.168.12.2 default-originate route-map r2
+ exit-address-family
+ !
+ address-family ipv6 labeled-unicast
+ neighbor 2001:db8:12::2 activate
+ neighbor 2001:db8:12::2 default-originate route-map r2
+ exit-address-family
+ !
+!
+route-map r2 permit 10
+ set community 65001:65001
+ set metric 666
+exit
+!
diff --git a/tests/topotests/bgp_labeled_unicast_default_originate/r1/zebra.conf b/tests/topotests/bgp_labeled_unicast_default_originate/r1/zebra.conf
new file mode 100644
index 0000000..686b075
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_default_originate/r1/zebra.conf
@@ -0,0 +1,5 @@
+!
+interface r1-eth0
+ ip address 192.168.12.1/24
+ ipv6 address 2001:db8:12::1/64
+!
diff --git a/tests/topotests/bgp_labeled_unicast_default_originate/r2/bgpd.conf b/tests/topotests/bgp_labeled_unicast_default_originate/r2/bgpd.conf
new file mode 100644
index 0000000..1498dff
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_default_originate/r2/bgpd.conf
@@ -0,0 +1,19 @@
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ no bgp default ipv6-unicast
+ neighbor 192.168.12.1 remote-as external
+ neighbor 192.168.12.1 timers 1 3
+ neighbor 192.168.12.1 timers connect 1
+ neighbor 2001:db8:12::1 remote-as external
+ neighbor 2001:db8:12::1 timers 1 3
+ neighbor 2001:db8:12::1 timers connect 1
+ address-family ipv4 labeled-unicast
+ neighbor 192.168.12.1 activate
+ exit-address-family
+ !
+ address-family ipv6 labeled-unicast
+ neighbor 2001:db8:12::1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_labeled_unicast_default_originate/r2/zebra.conf b/tests/topotests/bgp_labeled_unicast_default_originate/r2/zebra.conf
new file mode 100644
index 0000000..cb5c55e
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_default_originate/r2/zebra.conf
@@ -0,0 +1,5 @@
+!
+interface r2-eth0
+ ip address 192.168.12.2/24
+ ipv6 address 2001:db8:12::2/64
+!
diff --git a/tests/topotests/bgp_labeled_unicast_default_originate/test_bgp_labeled_unicast_default_originate.py b/tests/topotests/bgp_labeled_unicast_default_originate/test_bgp_labeled_unicast_default_originate.py
new file mode 100644
index 0000000..34c23d9
--- /dev/null
+++ b/tests/topotests/bgp_labeled_unicast_default_originate/test_bgp_labeled_unicast_default_originate.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Check if labeled-unicast works correctly with default-originate.
+"""
+
+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_labeled_unicast_default_originate():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _bgp_check_advertised_routes():
+ output = json.loads(
+ r1.vtysh_cmd(
+ "show bgp ipv4 labeled-unicast neighbors 192.168.12.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "bgpOriginatingDefaultNetwork": "0.0.0.0/0",
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_advertised_routes)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to advertise default route for labeled-unicast"
+
+ def _bgp_check_received_ipv4_routes():
+ output = json.loads(
+ r2.vtysh_cmd("show bgp ipv4 labeled-unicast 0.0.0.0/0 json")
+ )
+ expected = {
+ "paths": [
+ {
+ "valid": True,
+ "metric": 666,
+ "community": {
+ "string": "65001:65001",
+ },
+ "remoteLabel": 0,
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_received_ipv4_routes)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to receive IPv4 default route for labeled-unicast"
+
+ def _bgp_check_received_ipv6_routes():
+ output = json.loads(r2.vtysh_cmd("show bgp ipv6 labeled-unicast ::/0 json"))
+ expected = {
+ "paths": [
+ {
+ "valid": True,
+ "metric": 666,
+ "community": {
+ "string": "65001:65001",
+ },
+ "remoteLabel": 2,
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_received_ipv6_routes)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to receive IPv6 default route for labeled-unicast"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_large_comm_list_match/__init__.py b/tests/topotests/bgp_large_comm_list_match/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_large_comm_list_match/__init__.py
diff --git a/tests/topotests/bgp_large_comm_list_match/r1/bgpd.conf b/tests/topotests/bgp_large_comm_list_match/r1/bgpd.conf
new file mode 100644
index 0000000..1a91f0f
--- /dev/null
+++ b/tests/topotests/bgp_large_comm_list_match/r1/bgpd.conf
@@ -0,0 +1,28 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.2 remote-as external
+ neighbor 192.168.0.2 timers 1 3
+ neighbor 192.168.0.2 timers connect 1
+ address-family ipv4
+ redistribute connected
+ neighbor 192.168.0.2 route-map r2 out
+ exit-address-family
+!
+ip prefix-list p1 seq 5 permit 172.16.255.1/32
+ip prefix-list p3 seq 5 permit 172.16.255.3/32
+ip prefix-list p4 seq 5 permit 172.16.255.4/32
+!
+route-map r2 permit 10
+ match ip address prefix-list p1
+ set large-community 65001:1:1 65001:2:1
+route-map r2 permit 20
+ match ip address prefix-list p3
+ set large-community 65001:3:1
+route-map r2 permit 30
+ match ip address prefix-list p4
+ set large-community 65001:10:1 65001:12:1 65001:13:1
+exit
+route-map r2 permit 40
+exit
+!
diff --git a/tests/topotests/bgp_large_comm_list_match/r1/zebra.conf b/tests/topotests/bgp_large_comm_list_match/r1/zebra.conf
new file mode 100644
index 0000000..4219a7c
--- /dev/null
+++ b/tests/topotests/bgp_large_comm_list_match/r1/zebra.conf
@@ -0,0 +1,12 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+ ip address 172.16.255.2/32
+ ip address 172.16.255.3/32
+ ip address 172.16.255.4/32
+!
+interface r1-eth0
+ ip address 192.168.0.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_large_comm_list_match/r2/bgpd.conf b/tests/topotests/bgp_large_comm_list_match/r2/bgpd.conf
new file mode 100644
index 0000000..779b705
--- /dev/null
+++ b/tests/topotests/bgp_large_comm_list_match/r2/bgpd.conf
@@ -0,0 +1,24 @@
+!
+!debug bgp updates
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.1 remote-as external
+ neighbor 192.168.0.1 timers 1 3
+ neighbor 192.168.0.1 timers connect 1
+ neighbor 192.168.1.3 remote-as external
+ neighbor 192.168.1.3 timers 1 3
+ neighbor 192.168.1.3 timers connect 1
+ address-family ipv4
+ neighbor 192.168.0.1 route-map r1 in
+ neighbor 192.168.0.1 soft-reconfiguration inbound
+ exit-address-family
+!
+bgp large-community-list 1 seq 5 permit 65001:1:1 65001:2:1
+bgp large-community-list 1 seq 10 permit 65001:3:1
+!
+route-map r1 deny 10
+ match large-community 1
+route-map r1 permit 20
+exit
+!
diff --git a/tests/topotests/bgp_large_comm_list_match/r2/zebra.conf b/tests/topotests/bgp_large_comm_list_match/r2/zebra.conf
new file mode 100644
index 0000000..7fe82ba
--- /dev/null
+++ b/tests/topotests/bgp_large_comm_list_match/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
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_large_comm_list_match/r3/bgpd.conf b/tests/topotests/bgp_large_comm_list_match/r3/bgpd.conf
new file mode 100644
index 0000000..e7cb76a
--- /dev/null
+++ b/tests/topotests/bgp_large_comm_list_match/r3/bgpd.conf
@@ -0,0 +1,21 @@
+!
+!debug bgp updates
+!
+router bgp 65003
+ 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
+ address-family ipv4
+ neighbor 192.168.1.2 route-map r1 in
+ neighbor 192.168.1.2 soft-reconfiguration inbound
+ exit-address-family
+!
+bgp large-community-list 2 seq 10 permit 65001:12:1
+!
+route-map r1 deny 10
+ match large-community 2 any
+exit
+route-map r1 permit 20
+exit
+!
diff --git a/tests/topotests/bgp_large_comm_list_match/r3/zebra.conf b/tests/topotests/bgp_large_comm_list_match/r3/zebra.conf
new file mode 100644
index 0000000..755dd18
--- /dev/null
+++ b/tests/topotests/bgp_large_comm_list_match/r3/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r3-eth0
+ ip address 192.168.1.3/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_large_comm_list_match/test_bgp_large_comm_list_match.py b/tests/topotests/bgp_large_comm_list_match/test_bgp_large_comm_list_match.py
new file mode 100644
index 0000000..483c048
--- /dev/null
+++ b/tests/topotests/bgp_large_comm_list_match/test_bgp_large_comm_list_match.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright 2023 by 6WIND S.A.
+#
+
+"""
+Check if BGP large-community-list works
+when used as match rule in incoming route-maps.
+
+- case 1 should deny incoming updates with large-community-list 1
+bgp large-community-list 1 seq 5 permit 65001:1:1 65001:2:1
+bgp large-community-list 1 seq 10 permit 65001:3:1
+!
+route-map r1 deny 10
+ match large-community 1
+
+route-map test deny 10
+ match community 1
+
+- case 2 should deny incoming updates with any large-community-list 1
+bgp large-community-list 2 seq 10 permit 65001:12:1
+!
+route-map r1 deny 10
+ match large-community 2 any
+"""
+
+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, 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["r3"])
+ 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_large_comm_list_match():
+ 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 bgp ipv4 unicast neighbors 192.168.0.1 filtered-routes json"
+ )
+ )
+ expected = {
+ "receivedRoutes": {
+ "172.16.255.1/32": {
+ "path": "65001",
+ },
+ "172.16.255.3/32": {
+ "path": "65001",
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("BGP filtering check with large-community-list on R2")
+ 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 filter BGP UPDATES with large-community-list on R2"
+
+
+def test_bgp_large_comm_list_match_any():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r3"]
+
+ def _bgp_converge():
+ output = json.loads(
+ router.vtysh_cmd(
+ "show bgp ipv4 unicast neighbors 192.168.1.2 filtered-routes json"
+ )
+ )
+ expected = {
+ "receivedRoutes": {
+ "172.16.255.4/32": {
+ "path": "65002 65001",
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("BGP filtering check with large-community-list on R3")
+ 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 filter BGP UPDATES with large-community-list on R3"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
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..892b15a
--- /dev/null
+++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py
@@ -0,0 +1,1216 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+
+"""
+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..0044dbb
--- /dev/null
+++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py
@@ -0,0 +1,2220 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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..af6976b
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py
@@ -0,0 +1,575 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_linkbw_ip.py
+#
+# Copyright (c) 2020 by
+# Cumulus Networks, Inc
+# Vivek Venkatraman
+#
+
+"""
+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..988b579
--- /dev/null
+++ b/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_listen_on_multiple_addresses.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by Boeing Defence Australia
+# Adriano Marto Reis
+#
+
+"""
+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..d604871
--- /dev/null
+++ b/tests/topotests/bgp_llgr/test_bgp_llgr.py
@@ -0,0 +1,185 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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/__init__.py b/tests/topotests/bgp_local_as/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_local_as/__init__.py
diff --git a/tests/topotests/bgp_local_as/r1/bgpd.conf b/tests/topotests/bgp_local_as/r1/bgpd.conf
new file mode 100644
index 0000000..fa147d8
--- /dev/null
+++ b/tests/topotests/bgp_local_as/r1/bgpd.conf
@@ -0,0 +1,14 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as 65002
+ neighbor 192.168.1.2 local-as 65002
+ neighbor 192.168.1.2 timers 1 3
+ neighbor 192.168.1.2 timers connect 1
+ neighbor PG peer-group
+ neighbor PG remote-as 65003
+ neighbor PG local-as 65003
+ neighbor 192.168.2.2 peer-group PG
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_local_as/r1/zebra.conf b/tests/topotests/bgp_local_as/r1/zebra.conf
new file mode 100644
index 0000000..5b32fae
--- /dev/null
+++ b/tests/topotests/bgp_local_as/r1/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+interface r1-eth1
+ ip address 192.168.2.1/24
+!
diff --git a/tests/topotests/bgp_local_as/r2/bgpd.conf b/tests/topotests/bgp_local_as/r2/bgpd.conf
new file mode 100644
index 0000000..9c25bdf
--- /dev/null
+++ b/tests/topotests/bgp_local_as/r2/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as internal
+ neighbor 192.168.1.1 timers 1 3
+ neighbor 192.168.1.1 timers connect 1
+!
diff --git a/tests/topotests/bgp_local_as/r2/zebra.conf b/tests/topotests/bgp_local_as/r2/zebra.conf
new file mode 100644
index 0000000..0c95656
--- /dev/null
+++ b/tests/topotests/bgp_local_as/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_local_as/r3/bgpd.conf b/tests/topotests/bgp_local_as/r3/bgpd.conf
new file mode 100644
index 0000000..54ccd90
--- /dev/null
+++ b/tests/topotests/bgp_local_as/r3/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as internal
+ neighbor 192.168.2.1 timers 1 3
+ neighbor 192.168.2.1 timers connect 1
+!
diff --git a/tests/topotests/bgp_local_as/r3/zebra.conf b/tests/topotests/bgp_local_as/r3/zebra.conf
new file mode 100644
index 0000000..d28dedd
--- /dev/null
+++ b/tests/topotests/bgp_local_as/r3/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r3-eth0
+ ip address 192.168.2.2/24
+!
diff --git a/tests/topotests/bgp_local_as/test_bgp_local_as.py b/tests/topotests/bgp_local_as/test_bgp_local_as.py
new file mode 100644
index 0000000..9e5f146
--- /dev/null
+++ b/tests/topotests/bgp_local_as/test_bgp_local_as.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+
+"""
+
+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, 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["r1"])
+ 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_local_as_same_remote_as():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_check_local_as_same_remote_as():
+ output = json.loads(
+ tgen.gears["r2"].vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json")
+ )
+ expected = {
+ "paths": [
+ {
+ "valid": True,
+ "aspath": {"string": "Local"},
+ "nexthops": [{"ip": "192.168.1.1", "hostname": "r1"}],
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Check if iBGP works when local-as == remote-as")
+ test_func = functools.partial(_bgp_check_local_as_same_remote_as)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP prefixes on R2"
+
+
+def test_bgp_peer_group_local_as_same_remote_as():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_check_local_as_same_remote_as():
+ output = json.loads(
+ tgen.gears["r3"].vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json")
+ )
+ expected = {
+ "paths": [
+ {
+ "valid": True,
+ "aspath": {"string": "Local"},
+ "nexthops": [{"ip": "192.168.2.1", "hostname": "r1"}],
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Initial BGP converge")
+ test_func = functools.partial(_bgp_check_local_as_same_remote_as)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP prefixes on R3"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/__init__.py b/tests/topotests/bgp_local_as_dotplus_private_remove/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_local_as_dotplus_private_remove/__init__.py
diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf
new file mode 100644
index 0000000..1846df2
--- /dev/null
+++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 0.65000 as-notation dot+
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 0.1000
+ neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.2 local-as 0.500
+ address-family ipv4 unicast
+ neighbor 192.168.255.2 remove-private-AS
+ redistribute connected
diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf
new file mode 100644
index 0000000..0a283c0
--- /dev/null
+++ b/tests/topotests/bgp_local_as_dotplus_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_dotplus_private_remove/r2/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf
new file mode 100644
index 0000000..c9adfa4
--- /dev/null
+++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf
@@ -0,0 +1,5 @@
+router bgp 0.1000 as-notation dot+
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 0.500
+ neighbor 192.168.255.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_local_as_dotplus_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_dotplus_private_remove/r3/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/bgpd.conf
new file mode 100644
index 0000000..9a83127
--- /dev/null
+++ b/tests/topotests/bgp_local_as_dotplus_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_dotplus_private_remove/r3/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/zebra.conf
new file mode 100644
index 0000000..39499a1
--- /dev/null
+++ b/tests/topotests/bgp_local_as_dotplus_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_dotplus_private_remove/r4/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf
new file mode 100644
index 0000000..c9adfa4
--- /dev/null
+++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf
@@ -0,0 +1,5 @@
+router bgp 0.1000 as-notation dot+
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 0.500
+ neighbor 192.168.255.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf
new file mode 100644
index 0000000..b859115
--- /dev/null
+++ b/tests/topotests/bgp_local_as_dotplus_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_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py b/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py
new file mode 100644
index 0000000..930fd79
--- /dev/null
+++ b/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py
@@ -0,0 +1,129 @@
+#!/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 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["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)
+
+ r2 = tgen.gears["r2"]
+ r4 = tgen.gears["r4"]
+
+ 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",
+ }
+ }
+ 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 initially"
+
+ def _bgp_as_path(router, asn_path, asn_length):
+ output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.254/32 json"))
+ expected = {
+ "paths": [
+ {
+ "aspath": {
+ "string": asn_path,
+ "length": asn_length,
+ }
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_as_path, r2, "0.500", 1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Private ASNs not stripped"
+
+ test_func = functools.partial(_bgp_as_path, r4, "0.500 0.3000", 2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Private ASNs not stripped"
+
+
+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..9d22a79
--- /dev/null
+++ b/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# bgp_local_as_private_remove.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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 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["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)
+
+ r2 = tgen.gears["r2"]
+ r4 = tgen.gears["r4"]
+
+ 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",
+ }
+ }
+ 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 initially"
+
+ def _bgp_as_path(router, asn_path, asn_length):
+ output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.254/32 json"))
+ expected = {
+ "paths": [
+ {
+ "aspath": {
+ "string": asn_path,
+ "length": asn_length,
+ }
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_as_path, r2, "500", 1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Private ASNs not stripped"
+
+ test_func = functools.partial(_bgp_as_path, r4, "500 3000", 2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Private ASNs not stripped"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_local_asn/bgp_local_asn_agg.json b/tests/topotests/bgp_local_asn/bgp_local_asn_agg.json
new file mode 100644
index 0000000..a842c9e
--- /dev/null
+++ b/tests/topotests/bgp_local_asn/bgp_local_asn_agg.json
@@ -0,0 +1,147 @@
+{
+ "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"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {}}}
+ }
+ }
+ }
+ }
+ },
+ "static_routes":[
+ {
+ "network":"10.1.1.0/32",
+ "next_hop":"Null0"
+ },
+ {
+ "network":"10:1::1:0/128",
+ "next_hop":"Null0"
+ }]
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r3": {"dest_link": {"r2": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r3": {"dest_link": {"r2": {}}}
+ }
+ }
+ }
+ }
+ },
+ "static_routes":[
+ {
+ "network":"10.1.2.0/32",
+ "next_hop":"Null0"
+ },
+ {
+ "network":"10:1::2:0/128",
+ "next_hop":"Null0"
+ }]
+ },
+ "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": "300",
+ "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": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {}}}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_local_asn/bgp_local_asn_ecmp.json b/tests/topotests/bgp_local_asn/bgp_local_asn_ecmp.json
new file mode 100644
index 0000000..7184679
--- /dev/null
+++ b/tests/topotests/bgp_local_asn/bgp_local_asn_ecmp.json
@@ -0,0 +1,317 @@
+{
+ "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": {
+ "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":"10.0.0.1/32",
+ "next_hop":"Null0"
+ },
+ {
+ "network":"10::1/128",
+ "next_hop":"Null0"
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": [{
+ "local_as": "200",
+ "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": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link8": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": [{
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {},
+ "r3-link8": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {},
+ "r3-link8": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "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"
+ }
+ },
+ "bgp": [{
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link1": {},
+ "r4-link2": {},
+ "r4-link3": {},
+ "r4-link4": {},
+ "r4-link5": {},
+ "r4-link6": {},
+ "r4-link7": {},
+ "r4-link8": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link1": {},
+ "r4-link2": {},
+ "r4-link3": {},
+ "r4-link4": {},
+ "r4-link5": {},
+ "r4-link6": {},
+ "r4-link7": {},
+ "r4-link8": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_local_asn/bgp_local_asn_topo1.json b/tests/topotests/bgp_local_asn/bgp_local_asn_topo1.json
new file mode 100644
index 0000000..da665be
--- /dev/null
+++ b/tests/topotests/bgp_local_asn/bgp_local_asn_topo1.json
@@ -0,0 +1,132 @@
+{
+ "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": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {}}}
+ }
+ }
+ }
+ }
+ },
+ "static_routes":[
+ {
+ "network":"10.1.1.0/32",
+ "next_hop":"Null0"
+ },
+ {
+ "network":"10:1::1:0/128",
+ "next_hop":"Null0"
+ }]
+ },
+ "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"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "300",
+ "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": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {}}}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_local_asn/bgp_local_asn_topo2.json b/tests/topotests/bgp_local_asn/bgp_local_asn_topo2.json
new file mode 100644
index 0000000..51f9bb2
--- /dev/null
+++ b/tests/topotests/bgp_local_asn/bgp_local_asn_topo2.json
@@ -0,0 +1,117 @@
+{
+ "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": "12000100",
+ "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": "12000200",
+ "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"}
+ },
+ "bgp": {
+ "local_as": "12000300",
+ "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": "12000400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {}}}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo1.json b/tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo1.json
new file mode 100644
index 0000000..d415b63
--- /dev/null
+++ b/tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo1.json
@@ -0,0 +1,152 @@
+{
+ "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": {
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp":[
+ {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+
+ "r3": {"dest_link": {"r2": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+
+ "r3": {"dest_link": {"r2": {}}}
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network":"10.0.0.1/32",
+ "next_hop":"Null0"
+ },
+ {
+ "network":"10::1/128",
+ "next_hop":"Null0"
+ }]
+ },
+ "r3": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto","vrf": "BLUE"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {"r3": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {"r3": {}}}
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "300",
+ "vrf": "BLUE",
+ "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","vrf": "BLUE"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "400",
+ "vrf": "BLUE",
+ "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_local_asn/bgp_local_asn_vrf_topo2.json b/tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo2.json
new file mode 100644
index 0000000..311ccce
--- /dev/null
+++ b/tests/topotests/bgp_local_asn/bgp_local_asn_vrf_topo2.json
@@ -0,0 +1,128 @@
+{
+ "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": {
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "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": {"r2": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r2": {}}}
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto","vrf": "RED"},
+ "r4": {"ipv4": "auto", "ipv6": "auto","vrf": "BLUE"}
+ },
+ "vrfs":[{"name": "RED","id": "1"},
+ {"name": "BLUE","id": "2"}],
+ "bgp":
+ [
+ {
+ "local_as": "300",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {"r3": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {"r3": {}}}
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "300",
+ "vrf": "BLUE",
+ "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","vrf": "BLUE"}
+ },
+ "vrfs":[{"name": "BLUE","id": "1"}],
+ "bgp":
+ [
+ {
+ "local_as": "400",
+ "vrf": "BLUE",
+ "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_local_asn/test_bgp_local_asn_agg.py b/tests/topotests/bgp_local_asn/test_bgp_local_asn_agg.py
new file mode 100644
index 0000000..26e8fe9
--- /dev/null
+++ b/tests/topotests/bgp_local_asn/test_bgp_local_asn_agg.py
@@ -0,0 +1,407 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+
+"""
+Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking:
+1. Verify the BGP Local AS functionality by aggregating routes in between 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, "../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,
+ step,
+ check_address_types,
+ check_router_status
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ verify_bgp_rib,
+ create_router_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_1_1 = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"}
+NETWORK_1_2 = {"ipv4": "10.1.2.0/32", "ipv6": "10:1::2:0/128"}
+AGGREGATE_NW = {"ipv4": "10.1.0.0/16", "ipv6": "10:1::/96"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+
+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_local_asn_agg.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_verify_bgp_local_as_agg_in_EBGP_p0(request):
+ """
+ Verify the BGP Local AS functionality by aggregating routes in between eBGP Peers.
+ """
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Done in base config: Advertise prefix 10.1.1.0/24 from Router-1(AS-100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/120 from Router-1(AS-100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+ for addr_type in ADDR_TYPES:
+ input_static_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK_1_1[addr_type]}]}
+ }
+
+ input_static_verify_r2 = {
+ "r2": {"static_routes": [{"network": NETWORK_1_2[addr_type]}]}
+ }
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r2", input_static_verify_r2)
+ 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 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "aggregate_address": [
+ {
+ "network": AGGREGATE_NW[addr_type],
+ "summary": True,
+ "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 a summarised route on advertising router R3 "
+ "and receiving router R4 for both AFIs"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_static_agg_r1 = {
+ "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}
+ }
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": [NETWORK_1_1[addr_type]]}]}
+ }
+
+ input_static_r2 = {
+ "r2": {"static_routes": [{"network": [NETWORK_1_2[addr_type]]}]}
+ }
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_agg_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1", "r2"], [input_static_r1, input_static_r2]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-110 is got added in the AS list 110 {100,110,200} by following "
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "{100,110,200}"
+ for addr_type in ADDR_TYPES:
+ input_static_agg_r1 = {
+ "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}
+ }
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_static_agg_r1, aspath=aspath
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "{100,200}"
+ for addr_type in ADDR_TYPES:
+ input_static_agg_r1 = {
+ "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}
+ }
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_static_agg_r1, aspath=aspath
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "110 {100,200}"
+ for addr_type in ADDR_TYPES:
+ input_static_agg_r1 = {
+ "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}
+ }
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_static_agg_r1, aspath=aspath
+ )
+ 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_local_asn/test_bgp_local_asn_ecmp.py b/tests/topotests/bgp_local_asn/test_bgp_local_asn_ecmp.py
new file mode 100644
index 0000000..8a11570
--- /dev/null
+++ b/tests/topotests/bgp_local_asn/test_bgp_local_asn_ecmp.py
@@ -0,0 +1,511 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+#
+##########################################################################################################################################
+#
+# Testcases
+#
+###########################################################################################################################################
+###########################################################################################################################################
+#
+# 1.10.1.7. Verify the BGP Local AS functionality with ECMP on 8 links by adding no-prepend and replace-as command in between eBGP Peers.
+#
+#################################################################################################################################################
+
+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/"))
+
+# 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,
+ create_static_routes,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ check_address_types,
+ check_router_status,
+ create_static_routes,
+ verify_fib_routes,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ verify_bgp_rib,
+ create_router_bgp,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+BGP_CONVERGENCE = False
+NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+
+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_local_asn_ecmp.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_verify_bgp_local_as_in_ecmp_EBGP_p0(request):
+ """
+ Verify the BGP Local AS functionality with ECMP on 8 links by
+ adding no-prepend and replace-as command in between eBGP Peers.
+ """
+
+ 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("Base config is done as part of JSON")
+ dut = "r1"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_static_route = {
+ "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_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+ input_dict_static_route_redist = {
+ "r1": {
+ "bgp": [
+ {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_static_route_redist)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R1")
+ result = verify_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "local_asn": {"local_as": "110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R4.")
+ dest_link = {}
+ for link_no in range(1, 9):
+ link = "r3-link" + str(link_no)
+ dest_link[link] = {"local_asn": {"local_as": "110"}}
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {"r4": {"dest_link": dest_link}}
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R2 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r2_to_r3 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "local_asn": {"remote_as": "110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R4 towards R3.")
+ dest_link = {}
+ for link_no in range(1, 9):
+ link = "r4-link" + str(link_no)
+ dest_link[link] = {"local_asn": {"remote_as": "110"}}
+ for addr_type in ADDR_TYPES:
+ input_dict_r4_to_r3 = {
+ "r4": {
+ "bgp": [
+ {
+ "local_as": "400",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {"r3": {"dest_link": dest_link}}
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r4_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R3 & R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+ for dut in ["r3", "r4"]:
+ result = verify_fib_routes(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ 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 that AS-110 is got added in the AS list 110 200 100 by following "
+ " commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4.")
+ dest_link = {}
+ for link_no in range(1, 9):
+ link = "r3-link" + str(link_no)
+ dest_link[link] = {"local_asn": {"local_as": "110"}}
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {"r4": {"dest_link": dest_link}}
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R2")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4")
+ dest_link = {}
+ for link_no in range(1, 9):
+ link = "r3-link" + str(link_no)
+ dest_link[link] = {
+ "local_asn": {"local_as": "110", "no_prepend": True, "replace_as": True}
+ }
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {"r4": {"dest_link": dest_link}}
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ 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_local_asn/test_bgp_local_asn_topo1.py b/tests/topotests/bgp_local_asn/test_bgp_local_asn_topo1.py
new file mode 100644
index 0000000..25c9bee
--- /dev/null
+++ b/tests/topotests/bgp_local_asn/test_bgp_local_asn_topo1.py
@@ -0,0 +1,3642 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+##########################################################################################################
+#
+# Functionality Testcases
+#
+##########################################################################################################
+"""
+1. Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between eBGP Peers.
+2. Verify the BGP Local AS functionality by configuring 4 Byte AS at R3 and 2 Byte AS at R2 & R4 in between eBGP Peers.
+3. Verify that BGP Local AS functionality by performing graceful restart in between eBGP Peers.
+4. Verify the BGP Local AS functionality by adding another AS & by same AS with AS-Prepend command in between eBGP Peers.
+4. Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between iBGP Peers.
+5. Verify the BGP Local AS functionality with allowas-in in between iBGP Peers.
+6. Verify that BGP Local AS functionality by performing shut/ noshut on the interfaces in between BGP neighbors.
+7. Verify that BGP Local AS functionality by restarting BGP,Zebra and FRR services and
+ further restarting clear BGP * and shutdown BGP neighbor.
+8. Verify the BGP Local AS functionality with different AS configurations.
+9. Verify the BGP Local AS functionality with R3& R4 with different AS configurations.
+"""
+
+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/"))
+
+# 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,
+ create_static_routes,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ get_frr_ipv6_linklocal,
+ check_address_types,
+ check_router_status,
+ create_static_routes,
+ verify_fib_routes,
+ create_route_maps,
+ kill_router_daemons,
+ start_router_daemons,
+ shutdown_bringup_interface,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ clear_bgp_and_verify,
+ verify_bgp_rib,
+ modify_as_number,
+ create_router_bgp,
+ verify_bgp_advertised_routes_from_neighbor,
+ verify_graceful_restart,
+ verify_r_bit,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+NEXT_HOP_IP_GR = {"ipv4": "10.0.0.5", "ipv6": "fd00:0:0:1::2/64"}
+NEXT_HOP_IP_1 = {"ipv4": "10.0.0.101", "ipv6": "fd00::1"}
+NEXT_HOP_IP_2 = {"ipv4": "10.0.0.102", "ipv6": "fd00::2"}
+
+BGP_CONVERGENCE = False
+PREFERRED_NEXT_HOP = "link_local"
+KEEPALIVETIMER = 1
+HOLDDOWNTIMER = 3
+
+
+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_local_asn_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)
+
+
+##########################################################################################################
+#
+# Local APIs
+#
+##########################################################################################################
+
+
+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)
+
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ 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"]["{}".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_bgp_local_as_in_EBGP_p0(request):
+ """
+ Verify the BGP Local AS functionality by adding no-prepend and
+ replace-as command in between eBGP Peers.
+ """
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-110 is got added in the AS list 110 200 100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify advertised routes to R4 at R3")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r3"
+ aspath = "200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_EBGP_4B_AS_mid_2B_AS_p0(request):
+ """
+ Verify the BGP Local AS functionality by configuring 4 Byte AS
+ at R3 and 2 Byte AS at R2 & R4 in between eBGP Peers.
+ """
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "12000110"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {
+ "local_asn": {
+ "remote_as": "12000110"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-12000110 is got added in the AS list 12000110 200 100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "12000110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "12000110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify advertised routes to R4 at R3")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r3"
+ aspath = "200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "12000110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "12000110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_GR_EBGP_p0(request):
+ """
+ Verify that BGP Local AS functionality by performing graceful restart in between eBGP Peers.
+ """
+ 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 basic BGP Peerings between R1,R2,R3 and R4")
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_static_route = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP_GR[addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+ input_dict_static_route_redist = {
+ "r1": {
+ "bgp": [
+ {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_static_route_redist)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("Verify IPv4 and IPv6 static routes received on R1")
+ result = verify_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R2 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r2_to_r3 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "local_asn": {"remote_as": "110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R4 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r4_to_r3 = {
+ "r4": {
+ "bgp": [
+ {
+ "local_as": "400",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "local_asn": {"remote_as": "110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r4_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R3 & R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP_GR[addr_type],
+ }
+ ]
+ }
+ }
+ for dut in ["r3", "r4"]:
+ result = verify_fib_routes(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ 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 that AS-110 is got added in the AS list 110 200 100 by following "
+ " commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ """
+ GR Steps : Helper BGP router R2, mark and unmark IPV4 routes
+ as stale as the restarting router R3 come up within the restart time
+ """
+ # Create route-map to prefer global next-hop
+ input_dict = {
+ "r2": {
+ "route_maps": {
+ "rmap_global": [
+ {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}}
+ ]
+ }
+ },
+ "r3": {
+ "route_maps": {
+ "rmap_global": [
+ {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}}
+ ]
+ }
+ },
+ }
+ result = create_route_maps(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure neighbor for route map
+ input_dict_neigh_rm = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_neigh_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure graceful-restart
+ input_dict = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "graceful-restart-helper": True,
+ "local_asn": {"remote_as": "110"},
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "graceful-restart-helper": True,
+ "local_asn": {"remote_as": "110"},
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r3": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r3": {"graceful-restart": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r3", peer="r2")
+ 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)
+
+ # Verifying BGP RIB routes
+ dut = "r2"
+ peer = "r3"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global"
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r3"]}
+ 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
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, "bgp")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ logger.info("[Phase 2] : R3 goes for reload ")
+
+ kill_router_daemons(tgen, "r3", ["bgpd"])
+
+ logger.info(
+ "[Phase 3] : R3 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, preferred_next_hop="global"
+ )
+ 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 5] : R3 is about to come up now ")
+ start_router_daemons(tgen, "r3", ["bgpd"])
+
+ logger.info("[Phase 5] : R3 is UP Now ! ")
+
+ 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)
+
+ # Verifying GR stats
+ 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)
+
+ result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r3")
+ 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, preferred_next_hop="global"
+ )
+ 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)
+
+ step("Configure local-as with no-prepend at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R2")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_EBGP_aspath_p0(request):
+ """
+ Verify the BGP Local AS functionality by adding another AS & by same AS with AS-Prepend command in between eBGP Peers.
+ """
+ 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 basic BGP Peerings between R1,R2,R3 and R4")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-110 is got added in the AS list 110 200 100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify advertised routes to R4 at R3")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r3"
+ aspath = "200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure a route-map on R3 to prepend AS 2 times.")
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r3": {
+ "route_maps": {
+ "ASP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ "path": {"as_num": "1000 1000", "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 R4")
+ # Configure neighbor for route map
+ input_dict_7 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "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 local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(
+ "Verify that AS-300 is got replaced with 200 in the AS list 110 1000 1000 200 100 by following"
+ "commands at R3 router."
+ )
+ dut = "r4"
+ aspath = "110 1000 1000 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_iBGP_p0(request):
+ """
+ Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between iBGP Peers.
+ """
+ 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("Modify AS Number for R3")
+ input_dict_modify_as_number = {"r3": {"bgp": {"local_as": 200}}}
+ result = modify_as_number(tgen, topo, input_dict_modify_as_number)
+
+ step("Base config is done as part of JSON")
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_static_route = {
+ "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_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+ input_dict_static_route_redist = {
+ "r1": {
+ "bgp": [
+ {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_static_route_redist)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R1")
+ result = verify_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R4 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r4_to_r3 = {
+ "r4": {
+ "bgp": [
+ {
+ "local_as": "400",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "local_asn": {"remote_as": "110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r4_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R2 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r2_to_r3 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "next_hop_self": True,
+ "local_asn": {
+ "remote_as": "200",
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R3 & R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+ for dut in ["r3", "r4"]:
+ result = verify_fib_routes(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ 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 that AS-110 is got added in the AS list 110 200 100 by following "
+ " commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r4"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "110 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_allow_as_in_iBGP_p0(request):
+ """
+ Verify the BGP Local AS functionality with allowas-in in between iBGP Peers.
+ """
+ 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("Modidy AS Number for R4")
+ input_dict_modify_as_number = {"r4": {"bgp": {"local_as": 100}}}
+ result = modify_as_number(tgen, topo, input_dict_modify_as_number)
+
+ step("Base config is done as part of JSON")
+ dut = "r1"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_static_route = {
+ "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_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+ input_dict_static_route_redist = {
+ "r1": {
+ "bgp": [
+ {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_static_route_redist)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R1")
+ result = verify_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure allow-as at R4")
+ for addr_type in ADDR_TYPES:
+ allow_as_config_r4 = {
+ "r4": {
+ "bgp": [
+ {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "allowas-in": {
+ "number_occurences": 1
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+
+ step(
+ "Configuring allow-as for {} address-family on router R4 ".format(addr_type)
+ )
+ result = create_router_bgp(tgen, topo, allow_as_config_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # now modify the as in r4 and reconfig bgp in r3 with new remote as.
+ topo1 = deepcopy(topo)
+ topo1["routers"]["r4"]["bgp"]["local_as"] = "100"
+
+ delete_bgp = {"r3": {"bgp": {"delete": True}}}
+ result = create_router_bgp(tgen, topo1, delete_bgp)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ build_config_from_json(tgen, topo1, save_bkup=False)
+
+ step("Configure local-as at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R2 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r2_to_r3 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "local_asn": {"remote_as": "110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_r2_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R4 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r4_to_r3 = {
+ "r4": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "local_asn": {"remote_as": "110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_r4_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R3 & R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+ for dut in ["r3", "r4"]:
+ result = verify_fib_routes(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ 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 that AS-110 is got added in the AS list 110 200 100 by following "
+ " commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_no_prep_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_no_prep_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R2")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_no_prep_rep_as_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_no_prep_rep_as_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_EBGP_port_reset_p0(request):
+ """
+ Verify that BGP Local AS functionality by performing shut/ noshut on the interfaces in between BGP neighbors.
+ """
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Api call to modfiy BGP timers at R3")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_timers = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_timers)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-110 is got added in the AS list 110 200 100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify advertised routes at R3 towards R4")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for count in range(1, 1):
+ step("Iteration {}".format(count))
+ step("Shut down connecting interface between R3<<>>R4 on R3.")
+
+ intf1 = topo["routers"]["r3"]["links"]["r4"]["interface"]
+
+ interfaces = [intf1]
+ for intf in interfaces:
+ shutdown_bringup_interface(tgen, "r3", intf, False)
+
+ step(
+ "On R3, all BGP peering in respective vrf instances go down"
+ " when the interface is shut"
+ )
+
+ result = verify_bgp_convergence(tgen, topo, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "Expected Behaviour: BGP will not be converged \n "
+ "Error {}".format(tc_name, result)
+ )
+
+ step("BGP neighborship is verified after restart of r3")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(
+ "Verify that AS-110 is got added in the AS list 110 200 100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_EBGP_negative2_p0(request):
+ """
+ Verify the BGP Local AS functionality with different AS configurations.
+ """
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-110 is got added in the AS list 110 200 100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify advertised routes to R4 at R3")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that AS-110 is not prepended in the AS list 110 200 100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+ step("Verify that AS-300 is replaced with AS-110 at R3 router.")
+ dut = "r4"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # configure negative scenarios
+ step("Configure local-as at R3 towards R4.")
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "300"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ if "bgp" in topo["routers"]["r3"].keys():
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "Expected Behaviour: Cannot have local-as same as BGP AS number \n "
+ "Error {}".format(tc_name, result)
+ )
+
+ step("Configure another local-as at R3 towards R4.")
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "110",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ if "bgp" in topo["routers"]["r3"].keys():
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "Expected Behaviour: Cannot have local-as same as BGP AS number \n "
+ "Error {}".format(tc_name, result)
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_EBGP_negative3_p0(request):
+ """
+ Verify the BGP Local AS functionality with R3& R4 with different AS configurations.
+ """
+ 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 basic BGP Peerings between R1,R2,R3 and R4")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-110 is got added in the AS list 110 200 100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Perform Negative scenarios
+ step("Configure another local-as at R3 towards R4.")
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "300"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ if "bgp" in topo["routers"]["r3"].keys():
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "Expected Behaviour: Cannot have local-as same as BGP AS number \n "
+ "Error {}".format(tc_name, result)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_EBGP_restart_daemons_p0(request):
+ """
+ Verify that BGP Local AS functionality by restarting BGP,Zebra and FRR services and
+ further restarting clear BGP * and shutdown BGP neighbor.
+ """
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["200", "400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-110 is got added in the AS list 110 200 100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGPd daemon on R3.")
+ kill_router_daemons(tgen, "r3", ["bgpd"])
+
+ step("Bring up BGPd daemon on R3.")
+ start_router_daemons(tgen, "r3", ["bgpd"])
+
+ step(
+ "Verify that AS-110 is got added in the AS list 110 200 100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify advertised routes at R3 towards R4")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(
+ "Verify that AS-110 is not prepended in the AS list 200 100 by following "
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGPd daemon on R3.")
+ kill_router_daemons(tgen, "r3", ["bgpd"])
+
+ step("Bring up BGPd daemon on R3.")
+ start_router_daemons(tgen, "r3", ["bgpd"])
+
+ step(
+ "Verify that AS-110 is not prepended in the AS list 200 100 by following "
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(
+ "Verified that AS-300 is got replaced with original AS-110 at R4 by following commands"
+ )
+ dut = "r4"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verified that AS-300 is got replaced with original AS-110 at R4 by following commands"
+ )
+ dut = "r4"
+ aspath = "110 200 100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ 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_local_asn/test_bgp_local_asn_topo2.py b/tests/topotests/bgp_local_asn/test_bgp_local_asn_topo2.py
new file mode 100644
index 0000000..0c10c64
--- /dev/null
+++ b/tests/topotests/bgp_local_asn/test_bgp_local_asn_topo2.py
@@ -0,0 +1,686 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+##########################################################################################################
+#
+# Testcases
+#
+##########################################################################################################
+##########################################################################################################
+#
+# 1.10.1.2. Verify the BGP Local AS functionality by configuring 4 Byte AS in between eBGP Peers.
+#
+# 1.10.1.4. Verify the BGP Local AS functionality by configuring Old AS(local as) in 2 bytes and New AS in 4 bytes in between 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, "../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,
+ create_static_routes,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ check_address_types,
+ check_router_status,
+ create_static_routes,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ verify_bgp_rib,
+ create_router_bgp,
+ verify_bgp_advertised_routes_from_neighbor,
+)
+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": "10.1.1.0/32", "ipv6": "10:1::1:0/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+
+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_local_asn_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_verify_bgp_local_as_in_4_Byte_AS_EBGP_p0(request):
+ """
+ Verify the BGP Local AS functionality by configuring 4 Byte AS in between eBGP Peers.
+ """
+
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "12000300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "12000110"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(
+ ["r2", "r4"], ["12000200", "12000400"], ["r3", "r3"]
+ ):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {
+ "local_asn": {
+ "remote_as": "12000110"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step(
+ "Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-12000100)."
+ )
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-12000100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r2", "r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-12000110 is got added in the AS list 12000110 12000200 12000100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "12000110 12000200 12000100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "12000300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "12000110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify advertised routes to R4 at R3")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r3"
+ aspath = "12000200 12000100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "12000300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "12000110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "12000110 12000200 12000100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_old_AS2_new_AS4_EBGP_p0(request):
+ """
+ Verify the BGP Local AS functionality by configuring Old AS(local as) in
+ 2 bytes and New AS in 4 bytes in between eBGP Peers.
+ """
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "12000300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(
+ ["r2", "r4"], ["12000200", "12000400"], ["r3", "r3"]
+ ):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step(
+ "Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-12000100)."
+ )
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-12000100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-110 is got added in the AS list 110 12000200 12000100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 12000200 12000100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "12000300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify advertised routes to R4 at R3")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r3"
+ aspath = "12000200 12000100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "12000300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "110 12000200 12000100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ 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_local_asn/test_bgp_local_asn_vrf_topo1.py b/tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo1.py
new file mode 100644
index 0000000..ea6ab59
--- /dev/null
+++ b/tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo1.py
@@ -0,0 +1,1095 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+1. Verify the BGP Local AS functionality by adding new AS when dynamically import routes
+ from default vrf to non-default vrf with route map by adding AS by as-prepend command.
+2. Verify the BGP Local AS functionality by adding new AS when dynamically import routes
+ from non-default vrf to default vrf and further advertised to 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, "../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,
+ create_static_routes,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ check_address_types,
+ check_router_status,
+ create_static_routes,
+ verify_fib_routes,
+ create_route_maps,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ verify_bgp_rib,
+ create_router_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": "10.1.1.0/32", "ipv6": "10:1::1:0/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+
+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_local_asn_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_verify_local_asn_ipv4_import_from_default_to_non_default_VRF_p0(request):
+ """
+ Verify the BGP Local AS functionality by adding new AS when dynamically import routes
+ from default vrf to non-default vrf with route map by adding AS by as-prepend command.
+ """
+ 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("Base config is done as part of JSON")
+
+ # configure static routes
+ step("Advertise prefix 10.0.0.1/32 from Router-1(AS-100).")
+ step("Advertise an ipv6 prefix 10::1/128 from Router-1(AS-100).")
+ dut = "r2"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_static_route = {
+ "r2": {
+ "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_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R2")
+ input_dict_static_route_redist = {
+ "r2": {
+ "bgp": [
+ {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_static_route_redist)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure import vrf BLUE on R3 under IPv4 Address Family")
+ input_import_vrf_ipv4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": 300,
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {"unicast": {"import": {"vrf": "default"}}},
+ "ipv6": {"unicast": {"import": {"vrf": "default"}}},
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_import_vrf_ipv4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify IPv4 and IPv6 static routes received on R2")
+ for addr_type in ADDR_TYPES:
+ input_dict_static_route = {
+ "r2": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+ result = verify_rib(tgen, addr_type, "r2", input_dict_static_route)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r2", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r2", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "vrf": "BLUE",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R2 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r2_to_r3 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "local_asn": {"remote_as": "110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R4 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r4_to_r3 = {
+ "r4": {
+ "bgp": [
+ {
+ "local_as": "400",
+ "vrf": "BLUE",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "local_asn": {"remote_as": "110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r4_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R3 VRF BLUE & R4 VRF BLUE")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ }
+ ]
+ }
+ }
+ for dut in ["r3", "r4"]:
+ result = verify_fib_routes(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ 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 that AS-110 is got added in the AS list 110 200 100 by following "
+ " commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 200"
+ for addr_type in ADDR_TYPES:
+ input_static_r2 = {
+ "r2": {"static_routes": [{"network": NETWORK[addr_type], "vrf": "BLUE"}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure a route-map on R3 to prepend AS 2 times.")
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r3": {
+ "route_maps": {
+ "ASP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ "path": {"as_num": "1000 1000", "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 R4")
+ # Configure neighbor for route map
+ input_dict_7 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "vrf": "BLUE",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "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 local-as with no-prepend at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "vrf": "BLUE",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "200"
+ for addr_type in ADDR_TYPES:
+ input_static_r2 = {
+ "r2": {"static_routes": [{"network": NETWORK[addr_type], "vrf": "BLUE"}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R2")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "vrf": "BLUE",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "110 1000 1000 200"
+ for addr_type in ADDR_TYPES:
+ input_static_r2 = {
+ "r2": {"static_routes": [{"network": NETWORK[addr_type], "vrf": "BLUE"}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_local_asn_ipv4_import_from_non_default_to_default_VRF_p0(request):
+ """
+ Verify the BGP Local AS functionality by adding new AS when dynamically import routes
+ from non-default vrf to default vrf and further advertised to eBGP peers.
+ """
+ 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("Resetting the config from JSON")
+ reset_config_on_routers(tgen)
+
+ step("Base config is done as part of JSON")
+ # configure static routes
+ step("Advertise prefix 10.0.0.1/32 from Router-1(AS-100).")
+ step("Advertise an ipv6 prefix 10::1/128 from Router-1(AS-100).")
+ dut = "r4"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_static_route = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R4")
+ input_dict_static_route_redist = {
+ "r4": {
+ "bgp": {
+ "local_as": 400,
+ "vrf": "BLUE",
+ "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_static_route_redist)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure import from BLUE vrf to default vrf on R3 under IPv4 Address Family"
+ )
+ input_import_vrf_ipv4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": 300,
+ "vrf": "default",
+ "address_family": {
+ "ipv4": {"unicast": {"import": {"vrf": "BLUE"}}},
+ "ipv6": {"unicast": {"import": {"vrf": "BLUE"}}},
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_import_vrf_ipv4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify IPv4 and IPv6 static routes received on R3 VRF BLUE & R4 VRF BLUE")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ }
+ ]
+ }
+ }
+ for dut in ["r3", "r4"]:
+ result = verify_fib_routes(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "vrf": "BLUE",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R2 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r2_to_r3 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "local_asn": {"remote_as": "110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R4 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r4_to_r3 = {
+ "r4": {
+ "bgp": [
+ {
+ "local_as": "400",
+ "vrf": "BLUE",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "local_asn": {"remote_as": "110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r4_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R2")
+ for addr_type in ADDR_TYPES:
+ input_dict_static_route_from_r4 = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(tgen, addr_type, "r2", input_dict_static_route_from_r4)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r2", input_dict_static_route_from_r4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(
+ tgen, addr_type, "r2", input_dict_static_route_from_r4
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-110 is got added in the AS list 110 400 by following "
+ " commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 400"
+ for addr_type in ADDR_TYPES:
+ input_static_r2 = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "vrf": "BLUE",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+ dut = "r3"
+ aspath = "400"
+ for addr_type in ADDR_TYPES:
+ input_static_r2 = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R2")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "vrf": "BLUE",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r2"
+ aspath = "110 400"
+ for addr_type in ADDR_TYPES:
+ input_static_r2 = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath)
+ 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_local_asn/test_bgp_local_asn_vrf_topo2.py b/tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo2.py
new file mode 100644
index 0000000..b1204bf
--- /dev/null
+++ b/tests/topotests/bgp_local_asn/test_bgp_local_asn_vrf_topo2.py
@@ -0,0 +1,813 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+1. Verify the BGP Local AS functionality by adding new AS when leaking routes
+ from non-default VRF to non-default with route map by prefix lists.
+"""
+
+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 lib.topotest import version_cmp
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ create_static_routes,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ check_address_types,
+ check_router_status,
+ create_static_routes,
+ create_prefix_lists,
+ verify_fib_routes,
+ create_route_maps,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ verify_bgp_rib,
+ create_router_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": "10.1.1.0/32", "ipv6": "10:1::1:0/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+
+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_local_asn_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_verify_local_asn_ipv4_import_from_non_default_to_non_default_VRF_p0(request):
+ """
+ Verify the BGP Local AS functionality by adding new AS when leaking routes
+ from non-default VRF to non-default with route map by prefix lists.
+ """
+ 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("Base config is done as part of JSON")
+
+ # configure static routes
+ step("Advertise prefix 10.1.1.0/32 from Router-1(AS-100).")
+ step("Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-100).")
+ dut = "r2"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_static_route = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R2")
+ input_dict_static_route_redist = {
+ "r2": {
+ "bgp": {
+ "local_as": 200,
+ "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, input_dict_static_route_redist)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure import vrf BLUE from vrf RED on R3 under IPv4 Address Family")
+ input_import_vrf_ipv4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": 300,
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {"unicast": {"import": {"vrf": "RED"}}},
+ "ipv6": {"unicast": {"import": {"vrf": "RED"}}},
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_import_vrf_ipv4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify IPv4 and IPv6 static routes received on R3 VRF BLUE & R4 VRF BLUE")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ }
+ ]
+ }
+ }
+ for dut in ["r3", "r4"]:
+ result = verify_fib_routes(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r2 = {
+ "r3": {
+ "vrfs": [{"name": "RED", "id": "1"}],
+ "bgp": [
+ {
+ "local_as": "300",
+ "vrf": "RED",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ],
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r4 = {
+ "r3": {
+ "vrfs": [{"name": "BLUE", "id": "1"}],
+ "bgp": [
+ {
+ "local_as": "300",
+ "vrf": "BLUE",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ],
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R2 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r2_to_r3 = {
+ "r2": {
+ "vrfs": [{"name": "RED", "id": "1"}],
+ "bgp": [
+ {
+ "local_as": "200",
+ "vrf": "RED",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "local_asn": {"remote_as": "110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ],
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R4 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r4_to_r3 = {
+ "r4": {
+ "vrfs": [{"name": "BLUE", "id": "1"}],
+ "bgp": [
+ {
+ "local_as": "400",
+ "vrf": "BLUE",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "local_asn": {"remote_as": "110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ],
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r4_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R3 VRF BLUE & R4 VRF BLUE")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ }
+ ]
+ }
+ }
+ for dut in ["r3", "r4"]:
+ result = verify_fib_routes(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ 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 that AS-110 is got added in the AS list 110 200 100 by following "
+ " commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "110 200"
+ for addr_type in ADDR_TYPES:
+ input_static_r2 = {
+ "r2": {"static_routes": [{"network": NETWORK[addr_type], "vrf": "BLUE"}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # configure the prefix-list and route-maps.
+ for adt in ADDR_TYPES:
+ # Create Static routes
+ input_dict_rm1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[adt],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP_IP[adt],
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_rm1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Api call to redistribute static routes
+ input_dict_rm_rd = {
+ "r2": {
+ "bgp": {
+ "local_as": 200,
+ "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_rm_rd)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_rm_pl = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {
+ "seqid": 10,
+ "action": "permit",
+ "network": NETWORK["ipv4"],
+ }
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 100,
+ "action": "permit",
+ "network": NETWORK["ipv6"],
+ }
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_rm_pl)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_rm_r3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "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_rm_r3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_conf_neighbor_rm = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_tag_1_ipv4",
+ "direction": "out",
+ },
+ {
+ "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_conf_neighbor_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for adt in ADDR_TYPES:
+ # Verifying RIB routes
+ dut = "r3"
+ input_dict_r2_rib = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK[adt]],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP_IP[adt],
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_rib(tgen, adt, dut, input_dict_r2_rib)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ dut = "r4"
+ input_dict_r2_rib = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK[adt]],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP_IP[adt],
+ "vrf": "BLUE",
+ }
+ ]
+ }
+ }
+ result = verify_rib(tgen, adt, dut, input_dict_r2_rib)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r2 = {
+ "r3": {
+ "vrfs": [{"name": "RED", "id": "1"}],
+ "bgp": [
+ {
+ "local_as": "300",
+ "vrf": "RED",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ],
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r4 = {
+ "r3": {
+ "vrfs": [{"name": "BLUE", "id": "1"}],
+ "bgp": [
+ {
+ "local_as": "300",
+ "vrf": "BLUE",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ],
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "200"
+ for addr_type in ADDR_TYPES:
+ input_static_r2 = {
+ "r2": {"static_routes": [{"network": NETWORK[addr_type], "vrf": "BLUE"}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R2")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r2 = {
+ "r3": {
+ "vrfs": [{"name": "RED", "id": "1"}],
+ "bgp": [
+ {
+ "local_as": "300",
+ "vrf": "RED",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ],
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r4 = {
+ "r3": {
+ "vrfs": [{"name": "BLUE", "id": "1"}],
+ "bgp": [
+ {
+ "local_as": "300",
+ "vrf": "BLUE",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ],
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "110 200"
+ for addr_type in ADDR_TYPES:
+ input_static_r2 = {
+ "r2": {"static_routes": [{"network": NETWORK[addr_type], "vrf": "BLUE"}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r2, aspath=aspath)
+ 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_local_asn_dot/bgp_local_asn_dot_agg.json b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json
new file mode 100644
index 0000000..b481932
--- /dev/null
+++ b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json
@@ -0,0 +1,147 @@
+{
+ "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"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "1.100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {}}}
+ }
+ }
+ }
+ }
+ },
+ "static_routes":[
+ {
+ "network":"10.1.1.0/32",
+ "next_hop":"Null0"
+ },
+ {
+ "network":"10:1::1:0/128",
+ "next_hop":"Null0"
+ }]
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "1.200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r3": {"dest_link": {"r2": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r3": {"dest_link": {"r2": {}}}
+ }
+ }
+ }
+ }
+ },
+ "static_routes":[
+ {
+ "network":"10.1.2.0/32",
+ "next_hop":"Null0"
+ },
+ {
+ "network":"10:1::2:0/128",
+ "next_hop":"Null0"
+ }]
+ },
+ "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": "1.300",
+ "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": "1.400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {}}}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json
new file mode 100644
index 0000000..afacab4
--- /dev/null
+++ b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json
@@ -0,0 +1,317 @@
+{
+ "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": "1.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":"10.0.0.1/32",
+ "next_hop":"Null0"
+ },
+ {
+ "network":"10::1/128",
+ "next_hop":"Null0"
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": [{
+ "local_as": "1.200",
+ "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": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link8": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": [{
+ "local_as": "1.300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {},
+ "r3-link8": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {},
+ "r3-link8": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "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"
+ }
+ },
+ "bgp": [{
+ "local_as": "1.400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link1": {},
+ "r4-link2": {},
+ "r4-link3": {},
+ "r4-link4": {},
+ "r4-link5": {},
+ "r4-link6": {},
+ "r4-link7": {},
+ "r4-link8": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link1": {},
+ "r4-link2": {},
+ "r4-link3": {},
+ "r4-link4": {},
+ "r4-link5": {},
+ "r4-link6": {},
+ "r4-link7": {},
+ "r4-link8": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json
new file mode 100644
index 0000000..02aacf7
--- /dev/null
+++ b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json
@@ -0,0 +1,132 @@
+{
+ "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": "1.100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {}}}
+ }
+ }
+ }
+ }
+ },
+ "static_routes":[
+ {
+ "network":"10.1.1.0/32",
+ "next_hop":"Null0"
+ },
+ {
+ "network":"10:1::1:0/128",
+ "next_hop":"Null0"
+ }]
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "1.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"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "1.300",
+ "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": "1.400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {}}}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py
new file mode 100644
index 0000000..cb8fa1e
--- /dev/null
+++ b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py
@@ -0,0 +1,420 @@
+#!/usr/bin/env python3
+#
+# 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 BGP Multi-VRF Dynamic Route Leaking:
+1. Verify the BGP Local AS functionality by aggregating routes in between 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, "../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,
+ step,
+ check_address_types,
+ check_router_status
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ verify_bgp_rib,
+ create_router_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_1_1 = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"}
+NETWORK_1_2 = {"ipv4": "10.1.2.0/32", "ipv6": "10:1::2:0/128"}
+AGGREGATE_NW = {"ipv4": "10.1.0.0/16", "ipv6": "10:1::/96"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+
+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_local_asn_dot_agg.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_verify_bgp_local_as_agg_in_EBGP_p0(request):
+ """
+ Verify the BGP Local AS functionality by aggregating routes in between eBGP Peers.
+ """
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Done in base config: Advertise prefix 10.1.1.0/24 from Router-1(AS-1.100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/120 from Router-1(AS-1.100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+ for addr_type in ADDR_TYPES:
+ input_static_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK_1_1[addr_type]}]}
+ }
+
+ input_static_verify_r2 = {
+ "r2": {"static_routes": [{"network": NETWORK_1_2[addr_type]}]}
+ }
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r2", input_static_verify_r2)
+ 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 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "aggregate_address": [
+ {
+ "network": AGGREGATE_NW[addr_type],
+ "summary": True,
+ "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 a summarised route on advertising router R3 "
+ "and receiving router R4 for both AFIs"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_static_agg_r1 = {
+ "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}
+ }
+ input_static_r1 = {
+ "r1": {"static_routes": [{"network": [NETWORK_1_1[addr_type]]}]}
+ }
+
+ input_static_r2 = {
+ "r2": {"static_routes": [{"network": [NETWORK_1_2[addr_type]]}]}
+ }
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_agg_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1", "r2"], [input_static_r1, input_static_r2]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-110 is got added in the AS list 1.110 {1.100,1.110,1.200} by following "
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "{1.100,1.110,1.200}"
+ for addr_type in ADDR_TYPES:
+ input_static_agg_r1 = {
+ "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}
+ }
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_static_agg_r1, aspath=aspath
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "{1.100,1.200}"
+ for addr_type in ADDR_TYPES:
+ input_static_agg_r1 = {
+ "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}
+ }
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_static_agg_r1, aspath=aspath
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "1.110 {1.100,1.200}"
+ for addr_type in ADDR_TYPES:
+ input_static_agg_r1 = {
+ "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}
+ }
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_static_agg_r1, aspath=aspath
+ )
+ 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_local_asn_dot/test_bgp_local_asn_dot_ecmp.py b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py
new file mode 100644
index 0000000..6937a61
--- /dev/null
+++ b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py
@@ -0,0 +1,524 @@
+#!/usr/bin/env python3
+#
+# 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.
+#
+#
+##########################################################################################################################################
+#
+# Testcases
+#
+###########################################################################################################################################
+###########################################################################################################################################
+#
+# 1.10.1.7. Verify the BGP Local AS functionality with ECMP on 8 links by adding no-prepend and replace-as command in between eBGP Peers.
+#
+#################################################################################################################################################
+
+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/"))
+
+# 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,
+ create_static_routes,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ check_address_types,
+ check_router_status,
+ create_static_routes,
+ verify_fib_routes,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ verify_bgp_rib,
+ create_router_bgp,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+BGP_CONVERGENCE = False
+NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+
+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_local_asn_dot_ecmp.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_verify_bgp_local_as_in_ecmp_EBGP_p0(request):
+ """
+ Verify the BGP Local AS functionality with ECMP on 8 links by
+ adding no-prepend and replace-as command in between eBGP Peers.
+ """
+
+ 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("Base config is done as part of JSON")
+ dut = "r1"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_static_route = {
+ "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_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+ input_dict_static_route_redist = {
+ "r1": {
+ "bgp": [
+ {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_static_route_redist)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R1")
+ result = verify_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "local_asn": {"local_as": "1.110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R4.")
+ dest_link = {}
+ for link_no in range(1, 9):
+ link = "r3-link" + str(link_no)
+ dest_link[link] = {"local_asn": {"local_as": "1.110"}}
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {"r4": {"dest_link": dest_link}}
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R2 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r2_to_r3 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "1.200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "local_asn": {"remote_as": "1.110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R4 towards R3.")
+ dest_link = {}
+ for link_no in range(1, 9):
+ link = "r4-link" + str(link_no)
+ dest_link[link] = {"local_asn": {"remote_as": "1.110"}}
+ for addr_type in ADDR_TYPES:
+ input_dict_r4_to_r3 = {
+ "r4": {
+ "bgp": [
+ {
+ "local_as": "1.400",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {"r3": {"dest_link": dest_link}}
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r4_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R3 & R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+ for dut in ["r3", "r4"]:
+ result = verify_fib_routes(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ 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 that AS-110 is got added in the AS list 1.110 1.200 1.100 by following "
+ " commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4.")
+ dest_link = {}
+ for link_no in range(1, 9):
+ link = "r3-link" + str(link_no)
+ dest_link[link] = {"local_asn": {"local_as": "1.110"}}
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {"r4": {"dest_link": dest_link}}
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R2")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4")
+ dest_link = {}
+ for link_no in range(1, 9):
+ link = "r3-link" + str(link_no)
+ dest_link[link] = {
+ "local_asn": {"local_as": "1.110", "no_prepend": True, "replace_as": True}
+ }
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {"r4": {"dest_link": dest_link}}
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ 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_local_asn_dot/test_bgp_local_asn_dot_topo1.py b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py
new file mode 100644
index 0000000..e9234f5
--- /dev/null
+++ b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py
@@ -0,0 +1,3655 @@
+#!/usr/bin/env python3
+#
+# 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.
+#
+
+##########################################################################################################
+#
+# Functionality Testcases
+#
+##########################################################################################################
+"""
+1. Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between eBGP Peers.
+2. Verify the BGP Local AS functionality by configuring 4 Byte AS at R3 and 2 Byte AS at R2 & R4 in between eBGP Peers.
+3. Verify that BGP Local AS functionality by performing graceful restart in between eBGP Peers.
+4. Verify the BGP Local AS functionality by adding another AS & by same AS with AS-Prepend command in between eBGP Peers.
+4. Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between iBGP Peers.
+5. Verify the BGP Local AS functionality with allowas-in in between iBGP Peers.
+6. Verify that BGP Local AS functionality by performing shut/ noshut on the interfaces in between BGP neighbors.
+7. Verify that BGP Local AS functionality by restarting BGP,Zebra and FRR services and
+ further restarting clear BGP * and shutdown BGP neighbor.
+8. Verify the BGP Local AS functionality with different AS configurations.
+9. Verify the BGP Local AS functionality with R3& R4 with different AS configurations.
+"""
+
+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/"))
+
+# 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,
+ create_static_routes,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ get_frr_ipv6_linklocal,
+ check_address_types,
+ check_router_status,
+ create_static_routes,
+ verify_fib_routes,
+ create_route_maps,
+ kill_router_daemons,
+ start_router_daemons,
+ shutdown_bringup_interface,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ clear_bgp_and_verify,
+ verify_bgp_rib,
+ modify_as_number,
+ create_router_bgp,
+ verify_bgp_advertised_routes_from_neighbor,
+ verify_graceful_restart,
+ verify_r_bit,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+NEXT_HOP_IP_GR = {"ipv4": "10.0.0.5", "ipv6": "fd00:0:0:1::2/64"}
+NEXT_HOP_IP_1 = {"ipv4": "10.0.0.101", "ipv6": "fd00::1"}
+NEXT_HOP_IP_2 = {"ipv4": "10.0.0.102", "ipv6": "fd00::2"}
+
+BGP_CONVERGENCE = False
+PREFERRED_NEXT_HOP = "link_local"
+KEEPALIVETIMER = 1
+HOLDDOWNTIMER = 3
+
+
+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_local_asn_dot_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)
+
+
+##########################################################################################################
+#
+# Local APIs
+#
+##########################################################################################################
+
+
+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)
+
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ 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"]["{}".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_bgp_local_as_in_EBGP_p0(request):
+ """
+ Verify the BGP Local AS functionality by adding no-prepend and
+ replace-as command in between eBGP Peers.
+ """
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify advertised routes to R4 at R3")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r3"
+ aspath = "1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_EBGP_4B_AS_mid_4B_AS_p0(request):
+ """
+ Verify the BGP Local AS functionality by configuring 4 Byte AS
+ at R3 and 4 Byte AS at R2 & R4 in between eBGP Peers.
+ """
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "183.2926"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {
+ "local_asn": {
+ "remote_as": "183.2926"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-183.2926 is got added in the AS list 183.2926 1.200 1.100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "183.2926 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "183.2926",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify advertised routes to R4 at R3")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r3"
+ aspath = "1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "183.2926",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "183.2926 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_GR_EBGP_p0(request):
+ """
+ Verify that BGP Local AS functionality by performing graceful restart in between eBGP Peers.
+ """
+ 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 basic BGP Peerings between R1,R2,R3 and R4")
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_static_route = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP_GR[addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+ input_dict_static_route_redist = {
+ "r1": {
+ "bgp": [
+ {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_static_route_redist)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("Verify IPv4 and IPv6 static routes received on R1")
+ result = verify_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R2 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r2_to_r3 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "1.200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "local_asn": {"remote_as": "1.110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R4 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r4_to_r3 = {
+ "r4": {
+ "bgp": [
+ {
+ "local_as": "1.400",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "local_asn": {"remote_as": "1.110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r4_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R3 & R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP_GR[addr_type],
+ }
+ ]
+ }
+ }
+ for dut in ["r3", "r4"]:
+ result = verify_fib_routes(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ 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 that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following "
+ " commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ """
+ GR Steps : Helper BGP router R2, mark and unmark IPV4 routes
+ as stale as the restarting router R3 come up within the restart time
+ """
+ # Create route-map to prefer global next-hop
+ input_dict = {
+ "r2": {
+ "route_maps": {
+ "rmap_global": [
+ {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}}
+ ]
+ }
+ },
+ "r3": {
+ "route_maps": {
+ "rmap_global": [
+ {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}}
+ ]
+ }
+ },
+ }
+ result = create_route_maps(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure neighbor for route map
+ input_dict_neigh_rm = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_neigh_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure graceful-restart
+ input_dict = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "graceful-restart-helper": True,
+ "local_asn": {"remote_as": "1.110"},
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "graceful-restart-helper": True,
+ "local_asn": {"remote_as": "1.110"},
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r3": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r3": {"graceful-restart": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r3", peer="r2")
+ 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)
+
+ # Verifying BGP RIB routes
+ dut = "r2"
+ peer = "r3"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global"
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r3"]}
+ 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
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, "bgp")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ logger.info("[Phase 2] : R3 goes for reload ")
+
+ kill_router_daemons(tgen, "r3", ["bgpd"])
+
+ logger.info(
+ "[Phase 3] : R3 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, preferred_next_hop="global"
+ )
+ 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 5] : R3 is about to come up now ")
+ start_router_daemons(tgen, "r3", ["bgpd"])
+
+ logger.info("[Phase 5] : R3 is UP Now ! ")
+
+ 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)
+
+ # Verifying GR stats
+ 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)
+
+ result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r3")
+ 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, preferred_next_hop="global"
+ )
+ 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)
+
+ step("Configure local-as with no-prepend at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R2")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_EBGP_aspath_p0(request):
+ """
+ Verify the BGP Local AS functionality by adding another AS & by same AS with AS-Prepend command in between eBGP Peers.
+ """
+ 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 basic BGP Peerings between R1,R2,R3 and R4")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify advertised routes to R4 at R3")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r3"
+ aspath = "1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure a route-map on R3 to prepend AS 2 times.")
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r3": {
+ "route_maps": {
+ "ASP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ "path": {"as_num": "1.1000 1.1000", "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 R4")
+ # Configure neighbor for route map
+ input_dict_7 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "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 local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(
+ "Verify that AS-1.300 is got replaced with 1.200 in the AS list 1.110 1.1000 1.1000 1.200 1.100 by following"
+ "commands at R3 router."
+ )
+ dut = "r4"
+ aspath = "1.110 1.1000 1.1000 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_iBGP_p0(request):
+ """
+ Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between iBGP Peers.
+ """
+ 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("Modify AS Number for R3")
+ input_dict_modify_as_number = {"r3": {"bgp": {"local_as": "1.200"}}}
+ result = modify_as_number(tgen, topo, input_dict_modify_as_number)
+
+ step("Base config is done as part of JSON")
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_static_route = {
+ "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_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+ input_dict_static_route_redist = {
+ "r1": {
+ "bgp": [
+ {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_static_route_redist)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R1")
+ result = verify_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R4 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r4_to_r3 = {
+ "r4": {
+ "bgp": [
+ {
+ "local_as": "1.400",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "local_asn": {"remote_as": "1.110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r4_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R2 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r2_to_r3 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "1.200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "next_hop_self": True,
+ "local_asn": {
+ "remote_as": "1.200",
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R3 & R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+ for dut in ["r3", "r4"]:
+ result = verify_fib_routes(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ 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 that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following "
+ " commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r4"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "1.110 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_allow_as_in_iBGP_p0(request):
+ """
+ Verify the BGP Local AS functionality with allowas-in in between iBGP Peers.
+ """
+ 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("Modidy AS Number for R4")
+ input_dict_modify_as_number = {"r4": {"bgp": {"local_as": "1.100"}}}
+ result = modify_as_number(tgen, topo, input_dict_modify_as_number)
+
+ step("Base config is done as part of JSON")
+ dut = "r1"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_static_route = {
+ "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_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+ input_dict_static_route_redist = {
+ "r1": {
+ "bgp": [
+ {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_static_route_redist)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R1")
+ result = verify_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure allow-as at R4")
+ for addr_type in ADDR_TYPES:
+ allow_as_config_r4 = {
+ "r4": {
+ "bgp": [
+ {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "allowas-in": {
+ "number_occurences": 1
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+
+ step(
+ "Configuring allow-as for {} address-family on router R4 ".format(addr_type)
+ )
+ result = create_router_bgp(tgen, topo, allow_as_config_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # now modify the as in r4 and reconfig bgp in r3 with new remote as.
+ topo1 = deepcopy(topo)
+ topo1["routers"]["r4"]["bgp"]["local_as"] = "1.100"
+
+ delete_bgp = {"r3": {"bgp": {"delete": True}}}
+ result = create_router_bgp(tgen, topo1, delete_bgp)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ build_config_from_json(tgen, topo1, save_bkup=False)
+
+ step("Configure local-as at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R2 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r2_to_r3 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "1.200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "local_asn": {"remote_as": "1.110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_r2_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure remote-as at R4 towards R3.")
+ for addr_type in ADDR_TYPES:
+ input_dict_r4_to_r3 = {
+ "r4": {
+ "bgp": [
+ {
+ "local_as": "1.100",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "local_asn": {"remote_as": "1.110"}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_r4_to_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify IPv4 and IPv6 static routes received on R3 & R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+ for dut in ["r3", "r4"]:
+ result = verify_fib_routes(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ 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 that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following "
+ " commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R2.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_no_prep_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_no_prep_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R2")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r2 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_no_prep_rep_as_r3_to_r2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4")
+ for addr_type in ADDR_TYPES:
+ input_dict_no_prep_rep_as_r3_to_r4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo1, input_dict_no_prep_rep_as_r3_to_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_EBGP_port_reset_p0(request):
+ """
+ Verify that BGP Local AS functionality by performing shut/ noshut on the interfaces in between BGP neighbors.
+ """
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Api call to modfiy BGP timers at R3")
+ for addr_type in ADDR_TYPES:
+ input_dict_r3_timers = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3_timers)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify advertised routes at R3 towards R4")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for count in range(1, 1):
+ step("Iteration {}".format(count))
+ step("Shut down connecting interface between R3<<>>R4 on R3.")
+
+ intf1 = topo["routers"]["r3"]["links"]["r4"]["interface"]
+
+ interfaces = [intf1]
+ for intf in interfaces:
+ shutdown_bringup_interface(tgen, "r3", intf, False)
+
+ step(
+ "On R3, all BGP peering in respective vrf instances go down"
+ " when the interface is shut"
+ )
+
+ result = verify_bgp_convergence(tgen, topo, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "Expected Behaviour: BGP will not be converged \n "
+ "Error {}".format(tc_name, result)
+ )
+
+ step("BGP neighborship is verified after restart of r3")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(
+ "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r3"
+ aspath = "1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ dut = "r4"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_EBGP_negative2_p0(request):
+ """
+ Verify the BGP Local AS functionality with different AS configurations.
+ """
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify advertised routes to R4 at R3")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that AS-1.110 is not prepended in the AS list 1.110 1.200 1.100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+ step("Verify that AS-1.300 is replaced with AS-1.110 at R3 router.")
+ dut = "r4"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # configure negative scenarios
+ step("Configure local-as at R3 towards R4.")
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.300"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ if "bgp" in topo["routers"]["r3"].keys():
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "Expected Behaviour: Cannot have local-as same as BGP AS number \n "
+ "Error {}".format(tc_name, result)
+ )
+
+ step("Configure another local-as at R3 towards R4.")
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.110",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ if "bgp" in topo["routers"]["r3"].keys():
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "Expected Behaviour: Cannot have local-as same as BGP AS number \n "
+ "Error {}".format(tc_name, result)
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_EBGP_negative3_p0(request):
+ """
+ Verify the BGP Local AS functionality with R3& R4 with different AS configurations.
+ """
+ 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 basic BGP Peerings between R1,R2,R3 and R4")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Perform Negative scenarios
+ step("Configure another local-as at R3 towards R4.")
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.300"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ if "bgp" in topo["routers"]["r3"].keys():
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "Expected Behaviour: Cannot have local-as same as BGP AS number \n "
+ "Error {}".format(tc_name, result)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_local_as_in_EBGP_restart_daemons_p0(request):
+ """
+ Verify that BGP Local AS functionality by restarting BGP,Zebra and FRR services and
+ further restarting clear BGP * and shutdown BGP neighbor.
+ """
+ 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("Base config is done as part of JSON")
+ step("Configure local-as at R3 towards R4.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {"local_asn": {"local_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]):
+ input_dict_r2_r4 = {
+ dut: {
+ "bgp": {
+ "local_as": asn,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ dut: {"local_asn": {"remote_as": "1.110"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r2_r4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # configure static routes
+ step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).")
+ step(
+ "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)."
+ )
+ step("Verify that Static routes are redistributed in BGP process")
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_static_r1 = {
+ "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_static_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+ input_static_redist_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_static_redist_r1)
+ 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_verify_r1 = {
+ "r1": {"static_routes": [{"network": NETWORK[addr_type]}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r3", "r4"]:
+ result = verify_rib(tgen, addr_type, dut, input_static_r1)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, input_routes in zip(["r1"], [input_static_r1]):
+ result = verify_rib(tgen, addr_type, dut, input_routes)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGPd daemon on R3.")
+ kill_router_daemons(tgen, "r3", ["bgpd"])
+
+ step("Bring up BGPd daemon on R3.")
+ start_router_daemons(tgen, "r3", ["bgpd"])
+
+ step(
+ "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following"
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify advertised routes at R3 towards R4")
+ expected_routes = {
+ "ipv4": [
+ {"network": "10.1.1.0/32", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "10:1::1:0/128", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure local-as with no-prepend at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(
+ "Verify that AS-1.110 is not prepended in the AS list 1.200 1.100 by following "
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGPd daemon on R3.")
+ kill_router_daemons(tgen, "r3", ["bgpd"])
+
+ step("Bring up BGPd daemon on R3.")
+ start_router_daemons(tgen, "r3", ["bgpd"])
+
+ step(
+ "Verify that AS-1.110 is not prepended in the AS list 1.200 1.100 by following "
+ "commands at R3 router."
+ )
+ dut = "r3"
+ aspath = "1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.")
+ for addr_type in ADDR_TYPES:
+ for neighbor in ["r2", "r4"]:
+ input_dict_r3 = {
+ "r3": {
+ "bgp": {
+ "local_as": "1.300",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ neighbor: {
+ "dest_link": {
+ "r3": {
+ "local_asn": {
+ "local_as": "1.110",
+ "no_prepend": True,
+ "replace_as": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("BGP neighborship is verified by following commands in R3 routers")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(
+ "Verified that AS-1.300 is got replaced with original AS-1.110 at R4 by following commands"
+ )
+ dut = "r4"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verified that AS-1.300 is got replaced with original AS-1.110 at R4 by following commands"
+ )
+ dut = "r4"
+ aspath = "1.110 1.200 1.100"
+ for addr_type in ADDR_TYPES:
+ input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath)
+ 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_lu_explicitnull/r1/bgpd.conf b/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf
new file mode 100644
index 0000000..a31439c
--- /dev/null
+++ b/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf
@@ -0,0 +1,15 @@
+router bgp 65500
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ bgp labeled-unicast explicit-null
+ neighbor 192.0.2.2 remote-as 65501
+!
+ address-family ipv4 unicast
+ no neighbor 192.0.2.2 activate
+ network 192.168.2.1/32
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 192.0.2.2 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf b/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf
new file mode 100644
index 0000000..b845748
--- /dev/null
+++ b/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf
@@ -0,0 +1,6 @@
+interface r1-eth0
+ ip address 192.0.2.1/24
+!
+interface r1-eth1
+ ip address 192.168.2.1/32
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf b/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf
new file mode 100644
index 0000000..41c2b9b
--- /dev/null
+++ b/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf
@@ -0,0 +1,15 @@
+router bgp 65501
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ bgp labeled-unicast explicit-null
+ neighbor 192.0.2.1 remote-as 65500
+!
+ address-family ipv4 unicast
+ no neighbor 192.0.2.1 activate
+ network 192.168.2.2/32
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 192.0.2.1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf b/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf
new file mode 100644
index 0000000..9a63961
--- /dev/null
+++ b/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf
@@ -0,0 +1,6 @@
+interface r2-eth0
+ ip address 192.0.2.2/24
+!
+interface r2-eth1
+ ip address 192.168.2.2/32
+!
diff --git a/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py b/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py
new file mode 100644
index 0000000..0656e1e
--- /dev/null
+++ b/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# test_bgp_explicitnull.py
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright 2023 by 6WIND S.A.
+#
+
+"""
+test_bgp_lu_explicitnull.py: Test BGP LU label allocation
+"""
+
+import os
+import sys
+import json
+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
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Basic scenario for BGP-LU. Nodes are directly connected.
+# The 192.168.2.2/32 prefix is advertised from r2 to r1
+# The explicit-null label should be used
+# The 192.168.2.1/32 prefix is advertised from r1 to r2
+# The explicit-null label should be used
+# Traffic from 192.168.2.1 to 192.168.2.2 should use explicit-null label
+#
+# AS65500 BGP-LU AS65501
+# +-----+ +-----+
+# | |.1 .2| |
+# | 1 +----------------+ 2 + 192.168.0.2/32
+# | | 192.0.2.0/24 | |
+# +-----+ +-----+
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create routers
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+
+ # r1-r2
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # r1
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r1"])
+
+ # r2
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r2"])
+
+
+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)
+
+ # For all registred 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_show_ip_label_prefix_found(router, ipversion, prefix, label):
+ output = json.loads(
+ router.vtysh_cmd("show {} route {} json".format(ipversion, prefix))
+ )
+ expected = {
+ prefix: [
+ {"prefix": prefix, "nexthops": [{"fib": True, "labels": [int(label)]}]}
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+
+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 r1 gets prefix 192.168.2.2/32
+ test_func = functools.partial(
+ check_show_ip_label_prefix_found,
+ tgen.gears["r1"],
+ "ip",
+ "192.168.2.2/32",
+ "0",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r1, prefix 192.168.2.2/32 from r2 not present"
+
+ # Check r2 gets prefix 192.168.2.1/32
+ test_func = functools.partial(
+ check_show_ip_label_prefix_found,
+ tgen.gears["r2"],
+ "ip",
+ "192.168.2.1/32",
+ "0",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r2, prefix 192.168.2.1/32 from r1 not present"
+
+
+def test_traffic_connectivity():
+ "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 _check_ping(name, dest_addr, src_addr):
+ tgen = get_topogen()
+ output = tgen.gears[name].run(
+ "ping {} -c 1 -w 1 -I {}".format(dest_addr, src_addr)
+ )
+ logger.info(output)
+ if " 0% packet loss" not in output:
+ return True
+
+ logger.info("r1, check ping 192.168.2.2 from 192.168.2.1 is OK")
+ tgen = get_topogen()
+ func = functools.partial(_check_ping, "r1", "192.168.2.2", "192.168.2.1")
+ # tgen.mininet_cli()
+ success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
+ assert result is None, "r1, ping to 192.168.2.2 from 192.168.2.1 fails"
+
+
+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_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..c66571f
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json
@@ -0,0 +1,6 @@
+{
+ "ledger":506,
+ "inUse":506,
+ "requests":0,
+ "labelChunks":3
+}
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..d35e4ef
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json
@@ -0,0 +1,6 @@
+{
+ "ledger":0,
+ "inUse":0,
+ "requests":0,
+ "labelChunks":1
+}
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..7d59762
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/test_bgp_lu.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_lu.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+"""
+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..faeaa3e
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json
@@ -0,0 +1,6 @@
+{
+ "ledger":51,
+ "inUse":51,
+ "requests":0,
+ "labelChunks":1
+}
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..5f9d8e6
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/R2/labelpool.summ.json
@@ -0,0 +1,6 @@
+{
+ "ledger":1,
+ "inUse":1,
+ "requests":0,
+ "labelChunks":1
+}
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..13e6582
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/test_bgp_lu2.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# test_bgp_lu2.py
+#
+# Part of FRR/NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+# Copyright (c) 2021 by Nvidia, Inc.
+#
+
+"""
+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_max_med_on_startup/__init__.py b/tests/topotests/bgp_max_med_on_startup/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_max_med_on_startup/__init__.py
diff --git a/tests/topotests/bgp_max_med_on_startup/r1/bgpd.conf b/tests/topotests/bgp_max_med_on_startup/r1/bgpd.conf
new file mode 100644
index 0000000..41bf963
--- /dev/null
+++ b/tests/topotests/bgp_max_med_on_startup/r1/bgpd.conf
@@ -0,0 +1,11 @@
+!
+router bgp 65001
+ bgp max-med on-startup 5 777
+ 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
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_max_med_on_startup/r1/zebra.conf b/tests/topotests/bgp_max_med_on_startup/r1/zebra.conf
new file mode 100644
index 0000000..7c2ed09
--- /dev/null
+++ b/tests/topotests/bgp_max_med_on_startup/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/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_max_med_on_startup/r2/bgpd.conf b/tests/topotests/bgp_max_med_on_startup/r2/bgpd.conf
new file mode 100644
index 0000000..187713d
--- /dev/null
+++ b/tests/topotests/bgp_max_med_on_startup/r2/bgpd.conf
@@ -0,0 +1,7 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65001
+ neighbor 192.168.255.1 timers 3 10
+ !
+!
diff --git a/tests/topotests/bgp_max_med_on_startup/r2/zebra.conf b/tests/topotests/bgp_max_med_on_startup/r2/zebra.conf
new file mode 100644
index 0000000..fd45c48
--- /dev/null
+++ b/tests/topotests/bgp_max_med_on_startup/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_max_med_on_startup/test_bgp_max_med_on_startup.py b/tests/topotests/bgp_max_med_on_startup/test_bgp_max_med_on_startup.py
new file mode 100644
index 0000000..a9810ba
--- /dev/null
+++ b/tests/topotests/bgp_max_med_on_startup/test_bgp_max_med_on_startup.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_max_med_on_startup.py
+#
+# Copyright (c) 2022 Rubicon Communications, LLC.
+#
+
+"""
+Test whether `bgp max-med on-startup (5-86400) [(0-4294967295)]` 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_max_med_on_startup():
+ 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.1 json"))
+ expected = {"192.168.255.1": {"bgpState": "Established"}}
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_has_routes(router, metric):
+ output = json.loads(
+ router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 routes json")
+ )
+ expected = {"routes": {"172.16.255.254/32": [{"metric": metric}]}}
+ return topotest.json_cmp(output, expected)
+
+ # Check session is established
+ test_func = functools.partial(_bgp_converge, router2)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed bgp convergence on r2"
+
+ # Check metric has value of max-med
+ test_func = functools.partial(_bgp_has_routes, router2, 777)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "r2 does not receive routes with metric 777"
+
+ # Check that when the max-med timer expires, metric is updated
+ test_func = functools.partial(_bgp_has_routes, router2, 0)
+ success, result = topotest.run_and_expect(test_func, None, count=16, wait=0.5)
+ assert result is None, "r2 does not receive routes with metric 0"
+
+
+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..271a5bb
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_invalid_update/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
+ redistribute connected
+ neighbor 192.168.255.2 prefix-list r2 out
+ exit-address-family
+ !
+!
+ip prefix-list r2 seq 5 permit 172.16.255.253/32
+ip prefix-list r2 seq 10 permit 172.16.255.254/32
+!
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..68c5021
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+ ip address 172.16.255.253/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..cb30808
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf
@@ -0,0 +1,9 @@
+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
+ exit-address-family
+ !
+!
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..c6bdbc3
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# bgp_local_as_private_remove.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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
+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)
+
+ r2 = tgen.gears["r2"]
+
+ def _bgp_parsing_nlri():
+ output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "lastNotificationReason": "Cease/Maximum Number of Prefixes Reached",
+ "lastResetDueTo": "BGP Notification send",
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_parsing_nlri)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Didn't send NOTIFICATION when hitting maximum-prefix"
+
+
+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..0b346f6
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_maximum_prefix_out.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..9f4d968
--- /dev/null
+++ b/tests/topotests/bgp_minimum_holdtime/test_bgp_minimum_holdtime.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2021 by
+# Takemasa Imada <takemasa.imada@gmail.com>
+#
+
+"""
+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..0d92a3c
--- /dev/null
+++ b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py
@@ -0,0 +1,6277 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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..40a28fb
--- /dev/null
+++ b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py
@@ -0,0 +1,3839 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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..2f3421d
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py
@@ -0,0 +1,225 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_multiview_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2016 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+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_node_target_extcommunities/__init__.py b/tests/topotests/bgp_node_target_extcommunities/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_node_target_extcommunities/__init__.py
diff --git a/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf
new file mode 100644
index 0000000..8698338
--- /dev/null
+++ b/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf
@@ -0,0 +1,21 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
+router bgp 65001
+ bgp router-id 192.168.1.1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.3 remote-as external
+ neighbor 192.168.1.4 remote-as external
+ address-family ipv4 unicast
+ network 10.10.10.10/32
+ neighbor 192.168.1.2 route-map rmap out
+ neighbor 192.168.1.3 route-map rmap out
+ neighbor 192.168.1.4 route-map rmap out
+ exit-address-family
+!
+route-map rmap permit 10
+ set extcommunity nt 192.168.1.3:0 192.168.1.4:0
+exit
diff --git a/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf
new file mode 100644
index 0000000..09fda78
--- /dev/null
+++ b/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf
@@ -0,0 +1,8 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+router bgp 65002
+ bgp router-id 192.168.1.2
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
diff --git a/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf
new file mode 100644
index 0000000..4883f1f
--- /dev/null
+++ b/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf
@@ -0,0 +1,8 @@
+!
+int r3-eth0
+ ip address 192.168.1.3/24
+!
+router bgp 65003
+ bgp router-id 192.168.1.3
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
diff --git a/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf
new file mode 100644
index 0000000..f518bd1
--- /dev/null
+++ b/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf
@@ -0,0 +1,8 @@
+!
+int r4-eth0
+ ip address 192.168.1.4/24
+!
+router bgp 65004
+ bgp router-id 192.168.1.4
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
diff --git a/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py b/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py
new file mode 100644
index 0000000..23e820b
--- /dev/null
+++ b/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if Node Target Extended Communities works.
+
+At r1 we set NT to 192.168.1.3 and 192.168.1.4 (this is the R3/R4 router-id),
+and that means 10.10.10.10/32 MUST be installed on R3 and R4, but not on R2,
+because this route does not have NT:192.168.1.2.
+"""
+
+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.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_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_node_target_extended_communities():
+ 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"]
+
+ def _bgp_converge():
+ output = json.loads(r1.vtysh_cmd("show bgp summary json"))
+ expected = {
+ "ipv4Unicast": {
+ "peers": {
+ "192.168.1.2": {
+ "pfxSnt": 1,
+ "state": "Established",
+ },
+ "192.168.1.3": {
+ "pfxSnt": 1,
+ "state": "Established",
+ },
+ "192.168.1.4": {
+ "pfxSnt": 1,
+ "state": "Established",
+ },
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Failed announcing 10.10.10.10/32 to r2, r3, and r4"
+
+ def _bgp_check_route(router, exists):
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
+ if exists:
+ expected = {
+ "routes": {
+ "10.10.10.10/32": [
+ {
+ "valid": True,
+ }
+ ]
+ }
+ }
+ else:
+ expected = {
+ "routes": {
+ "10.10.10.10/32": None,
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_route, r3, True)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "10.10.10.10/32 is not installed, but SHOULD be"
+
+ test_func = functools.partial(_bgp_check_route, r4, True)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "10.10.10.10/32 is not installed, but SHOULD be"
+
+ test_func = functools.partial(_bgp_check_route, r2, False)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "10.10.10.10/32 is installed, but SHOULD NOT be"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
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..7f45a24
--- /dev/null
+++ b/tests/topotests/bgp_orf/test_bgp_orf.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+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_attribute_discard/__init__.py b/tests/topotests/bgp_path_attribute_discard/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/__init__.py
diff --git a/tests/topotests/bgp_path_attribute_discard/exabgp.env b/tests/topotests/bgp_path_attribute_discard/exabgp.env
new file mode 100644
index 0000000..28e6423
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/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_path_attribute_discard/peer1/exabgp.cfg b/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg
new file mode 100644
index 0000000..7fb9210
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg
@@ -0,0 +1,24 @@
+neighbor 10.0.0.1 {
+ router-id 10.0.0.2;
+ local-address 10.0.0.2;
+ local-as 65001;
+ peer-as 65002;
+
+ capability {
+ route-refresh;
+ }
+
+ static {
+ route 192.168.100.101/32 {
+ atomic-aggregate;
+ community 65001:101;
+ next-hop 10.0.0.2;
+ }
+
+ route 192.168.100.102/32 {
+ originator-id 10.0.0.2;
+ community 65001:102;
+ next-hop 10.0.0.2;
+ }
+ }
+}
diff --git a/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf b/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf
new file mode 100644
index 0000000..c96f354
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf
@@ -0,0 +1,6 @@
+!
+router bgp 65002
+ 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_path_attribute_discard/r1/zebra.conf b/tests/topotests/bgp_path_attribute_discard/r1/zebra.conf
new file mode 100644
index 0000000..51a1b26
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+!
diff --git a/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py b/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py
new file mode 100644
index 0000000..c97cd0b
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if `neighbor path-attribute discard` command works correctly,
+can discard unwanted attributes from UPDATE messages, and ignore them
+by continuing to process UPDATE messages.
+"""
+
+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):
+ 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_path_attribute_discard():
+ 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 ipv4 unicast json detail"))
+ expected = {
+ "routes": {
+ "192.168.100.101/32": {
+ "paths": [
+ {
+ "valid": True,
+ "atomicAggregate": True,
+ "community": {
+ "string": "65001:101",
+ },
+ }
+ ],
+ },
+ "192.168.100.102/32": {
+ "paths": [
+ {
+ "valid": True,
+ "originatorId": None,
+ "community": {
+ "string": "65001:102",
+ },
+ }
+ ],
+ },
+ }
+ }
+ 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 bgp convergence"
+
+ step("Discard atomic-aggregate, and community attributes from peer1")
+ r1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ neighbor 10.0.0.2 path-attribute discard 6 8
+ """
+ )
+
+ def _bgp_check_if_attributes_discarded():
+ output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json detail"))
+ expected = {
+ "routes": {
+ "192.168.100.101/32": {
+ "paths": [
+ {
+ "valid": True,
+ "atomicAggregate": None,
+ "community": None,
+ }
+ ],
+ },
+ "192.168.100.102/32": {
+ "paths": [
+ {
+ "valid": True,
+ "originatorId": None,
+ "community": None,
+ }
+ ],
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_if_attributes_discarded)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert (
+ result is None
+ ), "Failed to discard path attributes (atomic-aggregate, community)"
+
+
+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_path_attribute_treat_as_withdraw/__init__.py b/tests/topotests/bgp_path_attribute_treat_as_withdraw/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/__init__.py
diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/bgpd.conf b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/bgpd.conf
new file mode 100644
index 0000000..4286b98
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/bgpd.conf
@@ -0,0 +1,14 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.0.2 remote-as external
+ neighbor 10.0.0.2 timers 3 10
+ address-family ipv4 unicast
+ network 10.10.10.10/32 route-map atomic
+ network 10.10.10.20/32
+ exit-address-family
+!
+route-map atomic permit 10
+ set atomic-aggregate
+!
diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/zebra.conf b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/zebra.conf
new file mode 100644
index 0000000..51a1b26
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+!
diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/bgpd.conf b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/bgpd.conf
new file mode 100644
index 0000000..2e63fd8
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/bgpd.conf
@@ -0,0 +1,6 @@
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.1 remote-as external
+ neighbor 10.0.0.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/zebra.conf b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/zebra.conf
new file mode 100644
index 0000000..12d3731
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r2-eth0
+ ip address 10.0.0.2/24
+!
diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/test_bgp_path_attribute_treat_as_withdraw.py b/tests/topotests/bgp_path_attribute_treat_as_withdraw/test_bgp_path_attribute_treat_as_withdraw.py
new file mode 100644
index 0000000..a9d678a
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/test_bgp_path_attribute_treat_as_withdraw.py
@@ -0,0 +1,149 @@
+#!/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 `neighbor path-attribute treat-as-withdraw` command works correctly,
+can withdraw unwanted prefixes from BGP table.
+"""
+
+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):
+ r1 = tgen.add_router("r1")
+ r2 = tgen.add_router("r2")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(r1)
+ switch.add_link(r2)
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ r1 = tgen.gears["r1"]
+ r1.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf"))
+ r1.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf"))
+ r1.start()
+
+ r2 = tgen.gears["r2"]
+ r2.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r2/zebra.conf"))
+ r2.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r2/bgpd.conf"))
+ r2.start()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_path_attribute_treat_as_withdraw():
+ 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 bgp ipv4 unicast json detail"))
+ expected = {
+ "routes": {
+ "10.10.10.10/32": {
+ "paths": [
+ {
+ "valid": True,
+ "atomicAggregate": True,
+ }
+ ],
+ },
+ "10.10.10.20/32": {
+ "paths": [
+ {
+ "valid": True,
+ }
+ ],
+ },
+ }
+ }
+ 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, "Failed bgp convergence"
+
+ step("Withdraw prefixes with atomic-aggregate from r1")
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ neighbor 10.0.0.1 path-attribute treat-as-withdraw 6
+ """
+ )
+
+ def _bgp_check_if_route_withdrawn():
+ output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast json detail"))
+ expected = {
+ "routes": {
+ "10.10.10.10/32": None,
+ "10.10.10.20/32": {
+ "paths": [
+ {
+ "valid": True,
+ }
+ ],
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_if_route_withdrawn)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to withdraw prefixes with atomic-aggregate attribute"
+
+
+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_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..df39032
--- /dev/null
+++ b/tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py
@@ -0,0 +1,1528 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Modified work Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Original work Copyright (c) 2018 by Network Device Education
+# Foundation, Inc. ("NetDEF")
+#
+
+"""
+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_path_selection/__init__.py b/tests/topotests/bgp_path_selection/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/__init__.py
diff --git a/tests/topotests/bgp_path_selection/r1/bgpd.conf b/tests/topotests/bgp_path_selection/r1/bgpd.conf
new file mode 100644
index 0000000..08eb867
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/r1/bgpd.conf
@@ -0,0 +1,28 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.0.2.2 remote-as 65002
+ neighbor 192.0.2.2 timers 1 3
+ neighbor 192.0.2.2 timers connect 1
+ neighbor 192.0.2.2 ebgp-multihop 2
+ neighbor 192.0.2.3 remote-as 65002
+ neighbor 192.0.2.3 timers 1 3
+ neighbor 192.0.2.3 timers connect 1
+ neighbor 192.0.2.3 ebgp-multihop 2
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 192.0.2.2 activate
+ neighbor 192.0.2.3 activate
+ exit-address-family
+!
+router bgp 65001 vrf vrf1
+ bgp router-id 192.0.2.1
+ address-family ipv4 unicast
+ label vpn export 101
+ rd vpn export 101:1
+ rt vpn both 52:100
+ import vpn
+ export vpn
+ exit-address-family
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_path_selection/r1/ldpd.conf b/tests/topotests/bgp_path_selection/r1/ldpd.conf
new file mode 100644
index 0000000..04ae068
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/r1/ldpd.conf
@@ -0,0 +1,26 @@
+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 192.0.2.1
+ !
+ address-family ipv4
+ discovery transport-address 192.0.2.1
+ !
+ interface r1-eth0
+ !
+ interface r1-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/bgp_path_selection/r1/staticd.conf b/tests/topotests/bgp_path_selection/r1/staticd.conf
new file mode 100644
index 0000000..a37f60a
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/r1/staticd.conf
@@ -0,0 +1,2 @@
+ip route 192.0.2.2/32 192.168.1.2
+ip route 192.0.2.3/32 192.168.2.2 \ No newline at end of file
diff --git a/tests/topotests/bgp_path_selection/r1/zebra.conf b/tests/topotests/bgp_path_selection/r1/zebra.conf
new file mode 100644
index 0000000..e860d6e
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/r1/zebra.conf
@@ -0,0 +1,11 @@
+!
+interface lo
+ ip address 192.0.2.1/32
+ ip address 172.16.255.1/32
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+interface r1-eth1
+ ip address 192.168.2.1/24
+!
diff --git a/tests/topotests/bgp_path_selection/r2/bgpd.conf b/tests/topotests/bgp_path_selection/r2/bgpd.conf
new file mode 100644
index 0000000..cc878d8
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/r2/bgpd.conf
@@ -0,0 +1,25 @@
+router bgp 65002
+ no bgp network import-check
+ network 192.0.2.8/32
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as 65001
+ neighbor 192.168.1.1 timers 1 3
+ neighbor 192.168.1.1 timers connect 1
+ neighbor 192.168.1.1 ebgp-multihop 2
+ neighbor 192.168.1.1 update-source 192.0.2.2
+ address-family ipv4 vpn
+ neighbor 192.168.1.1 activate
+ exit-address-family
+!
+router bgp 65002 vrf vrf1
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 102
+ rd vpn export 102:1
+ rt vpn both 52:100
+ import vpn
+ export vpn
+ exit-address-family
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_path_selection/r2/ldpd.conf b/tests/topotests/bgp_path_selection/r2/ldpd.conf
new file mode 100644
index 0000000..67973ab
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/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 192.0.2.2
+ !
+ address-family ipv4
+ discovery transport-address 192.0.2.2
+ !
+ interface r2-eth0
+ !
+ interface r2-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/bgp_path_selection/r2/staticd.conf b/tests/topotests/bgp_path_selection/r2/staticd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/r2/staticd.conf
diff --git a/tests/topotests/bgp_path_selection/r2/zebra.conf b/tests/topotests/bgp_path_selection/r2/zebra.conf
new file mode 100644
index 0000000..40dfa98
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/r2/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 192.0.2.2/32
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_path_selection/r3/bgpd.conf b/tests/topotests/bgp_path_selection/r3/bgpd.conf
new file mode 100644
index 0000000..ca8d27a
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/r3/bgpd.conf
@@ -0,0 +1,25 @@
+router bgp 65002
+ no bgp network import-check
+ network 192.0.2.8/32
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as 65001
+ neighbor 192.168.2.1 timers 1 3
+ neighbor 192.168.2.1 timers connect 1
+ neighbor 192.168.2.1 ebgp-multihop 2
+ neighbor 192.168.1.1 update-source 192.0.2.3
+ address-family ipv4 vpn
+ neighbor 192.168.2.1 activate
+ exit-address-family
+!
+router bgp 65002 vrf vrf1
+ bgp router-id 192.0.2.3
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 103
+ rd vpn export 102:1
+ rt vpn both 52:100
+ import vpn
+ export vpn
+ exit-address-family
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_path_selection/r3/ldpd.conf b/tests/topotests/bgp_path_selection/r3/ldpd.conf
new file mode 100644
index 0000000..b282b08
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/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 192.0.2.3
+ !
+ address-family ipv4
+ discovery transport-address 192.0.2.3
+ !
+ interface r3-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/bgp_path_selection/r3/staticd.conf b/tests/topotests/bgp_path_selection/r3/staticd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/r3/staticd.conf
diff --git a/tests/topotests/bgp_path_selection/r3/zebra.conf b/tests/topotests/bgp_path_selection/r3/zebra.conf
new file mode 100644
index 0000000..a0473b6
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/r3/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 192.0.2.3/32
+!
+interface r3-eth0
+ ip address 192.168.2.2/24
+!
diff --git a/tests/topotests/bgp_path_selection/test_bgp_path_selection.py b/tests/topotests/bgp_path_selection/test_bgp_path_selection.py
new file mode 100644
index 0000000..bf5737b
--- /dev/null
+++ b/tests/topotests/bgp_path_selection/test_bgp_path_selection.py
@@ -0,0 +1,220 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Louis Scalbert <louis.scalbert@6wind.com>
+#
+
+"""
+
+"""
+
+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, 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["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for routern in range(1, 4):
+ tgen.gears["r{}".format(routern)].cmd("ip link add vrf1 type vrf table 10")
+ tgen.gears["r{}".format(routern)].cmd("ip link set vrf1 up")
+ tgen.gears["r{}".format(routern)].cmd("ip address add dev vrf1 {}.{}.{}.{}/32".format(routern, routern, routern,routern))
+ tgen.gears["r2"].cmd("ip address add dev vrf1 192.0.2.8/32")
+ tgen.gears["r3"].cmd("ip address add dev vrf1 192.0.2.8/32")
+
+ 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_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+ tgen.gears["r1"].cmd("ip route add 192.0.2.2 via 192.168.1.2 metric 20")
+ tgen.gears["r1"].cmd("ip route add 192.0.2.3 via 192.168.2.2 metric 20")
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+def test_bgp_path_selection_ecmp():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_check_path_selection_ecmp():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd("show bgp ipv4 unicast 192.0.2.8/32 json")
+ )
+ expected = {
+ "paths": [
+ {
+ "valid": True,
+ "aspath": {"string": "65002"},
+ "multipath": True,
+ "nexthops": [{"ip": "192.0.2.2", "metric": 20}],
+ },
+ {
+ "valid": True,
+ "aspath": {"string": "65002"},
+ "multipath": True,
+ "nexthops": [{"ip": "192.0.2.3", "metric": 20}],
+ }
+ ]
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ step("Check if two ECMP paths are present")
+ test_func = functools.partial(_bgp_check_path_selection_ecmp)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP prefixes on R1"
+
+
+def test_bgp_path_selection_vpn_ecmp():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_check_path_selection_vpn_ecmp():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv4 unicast 192.0.2.8/32 json")
+ )
+ expected = {
+ "paths": [
+ {
+ "valid": True,
+ "aspath": {"string": "65002"},
+ "multipath": True,
+ "nexthops": [{"ip": "192.0.2.2", "metric": 20}],
+ },
+ {
+ "valid": True,
+ "aspath": {"string": "65002"},
+ "multipath": True,
+ "nexthops": [{"ip": "192.0.2.3", "metric": 20}],
+ }
+ ]
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ step("Check if two ECMP paths are present")
+ test_func = functools.partial(_bgp_check_path_selection_vpn_ecmp)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP prefixes on R1"
+
+
+def test_bgp_path_selection_metric():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_check_path_selection_metric():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd("show bgp ipv4 unicast 192.0.2.8/32 json")
+ )
+ expected = {
+ "paths": [
+ {
+ "valid": True,
+ "aspath": {"string": "65002"},
+ "nexthops": [{"ip": "192.0.2.2", "metric": 10}],
+ "bestpath":{ "selectionReason":"IGP Metric"},
+ },
+ {
+ "valid": True,
+ "aspath": {"string": "65002"},
+ "nexthops": [{"ip": "192.0.2.3", "metric": 20}],
+ }
+ ]
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ tgen.gears["r1"].cmd("ip route add 192.0.2.2 via 192.168.1.2 metric 10")
+ tgen.gears["r1"].cmd("ip route del 192.0.2.2 via 192.168.1.2 metric 20")
+
+ step("Check if IGP metric best path is selected")
+ test_func = functools.partial(_bgp_check_path_selection_metric)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP prefixes on R1"
+
+
+def test_bgp_path_selection_vpn_metric():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_check_path_selection_vpn_metric():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv4 unicast 192.0.2.8/32 json")
+ )
+ expected = {
+ "paths": [
+ {
+ "valid": True,
+ "aspath": {"string": "65002"},
+ "nexthops": [{"ip": "192.0.2.2", "metric": 10}],
+ "bestpath":{ "selectionReason":"IGP Metric"},
+ },
+ {
+ "valid": True,
+ "aspath": {"string": "65002"},
+ "nexthops": [{"ip": "192.0.2.3", "metric": 20}],
+ }
+ ]
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ step("Check if IGP metric best path is selected")
+ test_func = functools.partial(_bgp_check_path_selection_vpn_metric)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP prefixes on R1"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_peer_graceful_shutdown/__init__.py b/tests/topotests/bgp_peer_graceful_shutdown/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_peer_graceful_shutdown/__init__.py
diff --git a/tests/topotests/bgp_peer_graceful_shutdown/r1/bgpd.conf b/tests/topotests/bgp_peer_graceful_shutdown/r1/bgpd.conf
new file mode 100644
index 0000000..8e59098
--- /dev/null
+++ b/tests/topotests/bgp_peer_graceful_shutdown/r1/bgpd.conf
@@ -0,0 +1,8 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_peer_graceful_shutdown/r1/zebra.conf b/tests/topotests/bgp_peer_graceful_shutdown/r1/zebra.conf
new file mode 100644
index 0000000..376a19a
--- /dev/null
+++ b/tests/topotests/bgp_peer_graceful_shutdown/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_peer_graceful_shutdown/r2/bgpd.conf b/tests/topotests/bgp_peer_graceful_shutdown/r2/bgpd.conf
new file mode 100644
index 0000000..0ee1821
--- /dev/null
+++ b/tests/topotests/bgp_peer_graceful_shutdown/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.2.2 remote-as internal
+ address-family ipv4 unicast
+ neighbor 192.168.2.2 next-hop-self
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_peer_graceful_shutdown/r2/zebra.conf b/tests/topotests/bgp_peer_graceful_shutdown/r2/zebra.conf
new file mode 100644
index 0000000..67ca02f
--- /dev/null
+++ b/tests/topotests/bgp_peer_graceful_shutdown/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.1/24
+!
diff --git a/tests/topotests/bgp_peer_graceful_shutdown/r3/bgpd.conf b/tests/topotests/bgp_peer_graceful_shutdown/r3/bgpd.conf
new file mode 100644
index 0000000..5945a02
--- /dev/null
+++ b/tests/topotests/bgp_peer_graceful_shutdown/r3/bgpd.conf
@@ -0,0 +1,5 @@
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as internal
+!
diff --git a/tests/topotests/bgp_peer_graceful_shutdown/r3/zebra.conf b/tests/topotests/bgp_peer_graceful_shutdown/r3/zebra.conf
new file mode 100644
index 0000000..e5a37c9
--- /dev/null
+++ b/tests/topotests/bgp_peer_graceful_shutdown/r3/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r3-eth0
+ ip address 192.168.2.2/24
+!
diff --git a/tests/topotests/bgp_peer_graceful_shutdown/test_bgp_peer_graceful_shutdown.py b/tests/topotests/bgp_peer_graceful_shutdown/test_bgp_peer_graceful_shutdown.py
new file mode 100644
index 0000000..2eb936a
--- /dev/null
+++ b/tests/topotests/bgp_peer_graceful_shutdown/test_bgp_peer_graceful_shutdown.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Check if routes from R1 has local-preference set to 0 and graceful-shutdown
+community.
+"""
+
+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 step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2"), "s2": ("r2", "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_orf():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+ r3 = tgen.gears["r3"]
+
+ def _bgp_converge():
+ output = json.loads(
+ r2.vtysh_cmd(
+ "show bgp ipv4 unicast neighbor 192.168.2.2 advertised-routes json"
+ )
+ )
+ expected = {"advertisedRoutes": {"10.10.10.1/32": {"locPrf": 100}}}
+ 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 at R2"
+
+ step("Mark routes from R1 as graceful-shutdown")
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ neighbor 192.168.1.1 graceful-shutdown
+ """
+ )
+
+ def _bgp_check_peer_graceful_shutdown():
+ output = json.loads(r3.vtysh_cmd("show bgp ipv4 unicast 10.10.10.1/32 json"))
+ expected = {
+ "paths": [
+ {
+ "locPrf": 0,
+ "community": {"string": "graceful-shutdown"},
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_peer_graceful_shutdown)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert (
+ result is None
+ ), "local-preference is not 0 and/or graceful-shutdown community missing"
+
+
+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..5a1340f
--- /dev/null
+++ b/tests/topotests/bgp_peer_group/r3/bgpd.conf
@@ -0,0 +1,11 @@
+!
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor PG peer-group
+ neighbor PG remote-as external
+ neighbor PG timers 3 10
+ neighbor 192.168.255.1 peer-group PG
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
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..a91fade
--- /dev/null
+++ b/tests/topotests/bgp_peer_group/test_bgp_peer-group.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed bgp convergence in r1"
+
+ def _bgp_peer_group_check_advertised_routes():
+ output = json.loads(
+ tgen.gears["r3"].vtysh_cmd("show ip bgp neighbor PG advertised-routes json")
+ )
+ expected = {
+ "advertisedRoutes": {
+ "192.168.255.0/24": {
+ "valid": True,
+ "best": True,
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_peer_group_check_advertised_routes)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed checking advertised routes from r3"
+
+
+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..ad6674c
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py
@@ -0,0 +1,372 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 Arista Networks, Inc.
+#
+
+"""
+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..7332059
--- /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/prefix_modify.json b/tests/topotests/bgp_prefix_list_topo1/prefix_modify.json
new file mode 100644
index 0000000..4a066ae
--- /dev/null
+++ b/tests/topotests/bgp_prefix_list_topo1/prefix_modify.json
@@ -0,0 +1,120 @@
+{
+ "address_types": ["ipv4"],
+ "ipv4base":"192.120.1.0",
+ "ipv4mask":24,
+ "link_ip_start":{"ipv4":"192.120.1.0", "v4mask":30, "ipv6":"fd00::", "v6mask":64},
+ "routers":{
+ "r1":{
+ "links":{
+ "lo": {"ipv4": "auto", "type": "loopback", "add_static_route":"yes"},
+ "r2":{"ipv4":"auto"},
+ "r3":{"ipv4":"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", "type": "loopback", "add_static_route":"yes"},
+ "r1":{"ipv4":"auto"},
+ "r2":{"ipv4":"auto"},
+ "r4":{"ipv4":"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", "type": "loopback", "add_static_route":"yes"},
+ "r3":{"ipv4":"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..f351dde
--- /dev/null
+++ b/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py
@@ -0,0 +1,1341 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+
+ 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+
+ 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+
+ 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+
+ 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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": "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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+
+ 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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": "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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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_prefix_list_topo1/test_prefix_modify.py b/tests/topotests/bgp_prefix_list_topo1/test_prefix_modify.py
new file mode 100644
index 0000000..541b9de
--- /dev/null
+++ b/tests/topotests/bgp_prefix_list_topo1/test_prefix_modify.py
@@ -0,0 +1,404 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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 modify prefix-list action
+"""
+
+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,
+ step,
+ create_route_maps,
+ check_router_status,
+)
+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]
+
+
+# Global variables
+bgp_convergence = False
+
+IPV4_PF3 = "192.168.0.0/18"
+IPV4_PF4 = "192.150.10.0/24"
+IPV4_PF5 = "192.168.10.1/32"
+IPV4_PF6 = "192.168.10.10/32"
+IPV4_PF7 = "192.168.10.0/24"
+
+
+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_bug_prefix_lists_deny_to_permit_p1(request):
+ """
+ Verify modification of prefix-list action
+ """
+
+ 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
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ # base config
+ step("Configure IPV4 and IPv6 IBGP and EBGP session as mentioned in setup")
+ step("Configure static routes on R2 with Null 0 nexthop")
+ input_dict_1 = {
+ "r2": {
+ "static_routes": [
+ {"network": IPV4_PF7, "no_of_ip": 1, "next_hop": "Null0"},
+ {"network": IPV4_PF6, "no_of_ip": 1, "next_hop": "Null0"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Advertise static route in BGP using redistribute static command")
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "All the static route advertised in R4 as BGP "
+ "routes verify using 'show ip bgp'and 'show bgp'"
+ )
+ dut = "r4"
+ protocol = "bgp"
+
+ input_dict_route = {
+ "r4": {"static_routes": [{"network": IPV4_PF7}, {"network": IPV4_PF6}]}
+ }
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_route)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure IPv4 and IPv6 prefix-list")
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": "5", "network": IPV4_PF7, "action": "deny"}
+ ],
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {"seqid": "10", "network": IPV4_PF7, "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "configure route-map seq to permit IPV4 prefix list and seq"
+ "2 to permit IPV6 prefix list and apply it to out direction on R3"
+ )
+
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_7 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1",
+ "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 on R4 should not have any IPv4 and IPv6 BGP routes using "
+ "show ip bgp show bgp"
+ )
+
+ dut = "r4"
+ protocol = "bgp"
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_route, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "Error : Routes are still present \n {}".format(
+ tc_name, result
+ )
+
+ step("Modify IPv4/IPv6 prefix-list sequence 5 to another value on R3")
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {"seqid": "5", "network": IPV4_PF4, "action": "deny"}
+ ],
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify /24 and /120 routes present on"
+ "R4 BGP table using show ip bgp show bgp"
+ )
+ input_dict = {"r4": {"static_routes": [{"network": IPV4_PF7}]}}
+
+ 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)
+
+ step("Change prefix-list to same as original on R3")
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {"seqid": "5", "network": IPV4_PF7, "action": "deny"}
+ ],
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify /24 and /120 routes removed on"
+ "R4 BGP table using show ip bgp show bgp"
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "Error : Routes are still present \n {}".format(
+ tc_name, result
+ )
+
+ step("Modify IPv4/IPv6 prefix-list sequence 5 to another value")
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {"seqid": "5", "network": IPV4_PF4, "action": "deny"}
+ ],
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(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)
+
+ step("Clear BGP on R3 and verify the routes")
+ clear_bgp(tgen, "ipv4", "r3")
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("On R3 add prefix-list permit any for IPv4 and IPv6 seq 15")
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {"seqid": "15", "network": "any", "action": "permit"}
+ ],
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify /24 and /32 /120 and /128 routes are present on R4")
+ result = verify_rib(tgen, "ipv4", dut, input_dict_route)
+ 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_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..bfc083b
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py
@@ -0,0 +1,172 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# 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>
+#
+
+"""
+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..d6b9432
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# 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>
+#
+
+"""
+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..35459f6
--- /dev/null
+++ b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py
@@ -0,0 +1,2407 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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..97366eb
--- /dev/null
+++ b/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_reject_as_sets.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..e48f81c
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as/test_bgp_remove_private_as.py
@@ -0,0 +1,415 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_remove_private_as.py
+#
+# Copyright (C) 2022 NVIDIA Corporation
+# Trey Aspelund
+#
+
+"""
+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")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_remove_private_as_route_map/__init__.py b/tests/topotests/bgp_remove_private_as_route_map/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as_route_map/__init__.py
diff --git a/tests/topotests/bgp_remove_private_as_route_map/r1/frr.conf b/tests/topotests/bgp_remove_private_as_route_map/r1/frr.conf
new file mode 100644
index 0000000..b2dba7d
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as_route_map/r1/frr.conf
@@ -0,0 +1,10 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
+router bgp 65001
+ 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
+!
diff --git a/tests/topotests/bgp_remove_private_as_route_map/r2/frr.conf b/tests/topotests/bgp_remove_private_as_route_map/r2/frr.conf
new file mode 100644
index 0000000..9c423ce
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as_route_map/r2/frr.conf
@@ -0,0 +1,19 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+ ip address 192.168.2.1/32
+!
+router bgp 65002
+ 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
+ redistribute connected
+ neighbor 192.168.1.1 route-map r1 out
+ neighbor 192.168.1.1 remove-private-AS all
+ exit-address-family
+!
+route-map r1 permit 10
+ set as-path prepend 65123 4200000001
+!
diff --git a/tests/topotests/bgp_remove_private_as_route_map/test_bgp_remove_private_as_route_map.py b/tests/topotests/bgp_remove_private_as_route_map/test_bgp_remove_private_as_route_map.py
new file mode 100644
index 0000000..2ae6f7f
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as_route_map/test_bgp_remove_private_as_route_map.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if private AS is removed from AS_PATH attribute when route-map is used (prepend).
+"""
+
+import os
+import re
+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_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_remove_private_as_route_map():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ def _check_routes():
+ output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json"))
+ expected = {
+ "routes": {
+ "192.168.2.1/32": [
+ {
+ "valid": True,
+ "path": "65002",
+ }
+ ]
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _check_routes,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert (
+ result is None
+ ), "65123 4200000001 ASNs should be removed from AS_PATH attribute"
+
+
+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..c789fa8
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/customize.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017-2018 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+# Modified by LabN Consulting, L.L.C.
+#
+
+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..7534dce
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+
+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..5c7cc8e
--- /dev/null
+++ b/tests/topotests/bgp_rmap_extcommunity_none/test_bgp_rmap_extcommunity_none.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..52fda69
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/test_bgp_roles_capability.py
@@ -0,0 +1,172 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+#
+# 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")
+#
+
+"""
+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..b371586
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py
@@ -0,0 +1,131 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+#
+# 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")
+#
+
+"""
+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..412ecc1
--- /dev/null
+++ b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py
@@ -0,0 +1,1160 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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..e300e8d
--- /dev/null
+++ b/tests/topotests/bgp_route_map/test_route_map_topo1.py
@@ -0,0 +1,1393 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+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 \n "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, dut, 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 \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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..95906ec
--- /dev/null
+++ b/tests/topotests/bgp_route_map/test_route_map_topo2.py
@@ -0,0 +1,3958 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""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 \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 \n "
+ "Expected: BGP attributes should not be set in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 \n "
+ "Expected: BGP attributes should not be set in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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 \n "
+ "Expected: Routes should not be present in {} FIB \n "
+ "Found: {}".format(tc_name, dut, 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..e5325c9
--- /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..a06e3ed
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_ipv6_nexthop/test_bgp_route_map_match_ipv6_nexthop.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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_map_match_source_protocol/__init__.py b/tests/topotests/bgp_route_map_match_source_protocol/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/__init__.py
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf
new file mode 100644
index 0000000..7c3efee
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf
@@ -0,0 +1,32 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+interface r1-eth1
+ ip address 192.168.2.1/24
+!
+ip route 10.10.10.10/32 192.168.2.2
+!
+router bgp 65001
+ 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
+ address-family ipv4
+ redistribute connected
+ redistribute static
+ neighbor 192.168.1.2 route-map r2 out
+ neighbor 192.168.2.2 route-map r3 out
+ exit-address-family
+!
+route-map r2 permit 10
+ match source-protocol static
+route-map r3 permit 10
+ match source-protocol connected
+!
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf
new file mode 100644
index 0000000..7213975
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf
@@ -0,0 +1,10 @@
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+!
+router bgp 65002
+ 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
+!
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf
new file mode 100644
index 0000000..4a1d830
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf
@@ -0,0 +1,10 @@
+!
+interface r3-eth0
+ ip address 192.168.2.2/24
+!
+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
+!
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py
new file mode 100644
index 0000000..2828796
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if r1 can announce only static routes to r2, and only connected
+routes to r3 using `match source-protocol` with 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 = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r1"])
+ 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_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_route_map_match_source_protocol():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_check_advertised_routes_r2():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd(
+ "show bgp ipv4 unicast neighbors 192.168.1.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "10.10.10.10/32": {
+ "valid": True,
+ }
+ },
+ "totalPrefixCounter": 1,
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_advertised_routes_r2)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Failed to filter routes by source-protocol for r2"
+
+ def _bgp_check_advertised_routes_r3():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd(
+ "show bgp ipv4 unicast neighbors 192.168.2.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "192.168.1.0/24": {
+ "valid": True,
+ },
+ "192.168.2.0/24": {
+ "valid": True,
+ },
+ "172.16.255.1/32": {
+ "valid": True,
+ },
+ },
+ "totalPrefixCounter": 3,
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_advertised_routes_r3)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Failed to filter routes by source-protocol for r3"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_route_map_on_match_next/__init__.py b/tests/topotests/bgp_route_map_on_match_next/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_route_map_on_match_next/__init__.py
diff --git a/tests/topotests/bgp_route_map_on_match_next/r1/bgpd.conf b/tests/topotests/bgp_route_map_on_match_next/r1/bgpd.conf
new file mode 100644
index 0000000..b858fff
--- /dev/null
+++ b/tests/topotests/bgp_route_map_on_match_next/r1/bgpd.conf
@@ -0,0 +1,10 @@
+!
+router bgp 65001
+ no bgp network import-check
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ address-family ipv4 unicast
+ network 10.100.100.1/32
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_route_map_on_match_next/r1/zebra.conf b/tests/topotests/bgp_route_map_on_match_next/r1/zebra.conf
new file mode 100644
index 0000000..581e2e6
--- /dev/null
+++ b/tests/topotests/bgp_route_map_on_match_next/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 192.168.255.1/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_route_map_on_match_next/r2/bgpd.conf b/tests/topotests/bgp_route_map_on_match_next/r2/bgpd.conf
new file mode 100644
index 0000000..518d63d
--- /dev/null
+++ b/tests/topotests/bgp_route_map_on_match_next/r2/bgpd.conf
@@ -0,0 +1,17 @@
+!
+router bgp 65001
+ neighbor 192.168.255.1 remote-as 65001
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4 unicast
+ neighbor 192.168.255.1 route-map RM in
+ exit-address-family
+ !
+!
+route-map RM permit 10
+ set weight 100
+exit
+!
+route-map RM permit 20
+ set metric 20
+exit
+!
diff --git a/tests/topotests/bgp_route_map_on_match_next/r2/zebra.conf b/tests/topotests/bgp_route_map_on_match_next/r2/zebra.conf
new file mode 100644
index 0000000..fd45c48
--- /dev/null
+++ b/tests/topotests/bgp_route_map_on_match_next/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_route_map_on_match_next/test_bgp_route_map_on_match_next.py b/tests/topotests/bgp_route_map_on_match_next/test_bgp_route_map_on_match_next.py
new file mode 100644
index 0000000..8fe45a3
--- /dev/null
+++ b/tests/topotests/bgp_route_map_on_match_next/test_bgp_route_map_on_match_next.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_route_map_on_match_next.py
+#
+# Copyright (c) 2023 Rubicon Communications, LLC.
+#
+
+"""
+Test whether `on-match next` added to an existing route-map entry takes effect.
+"""
+
+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_route_map_on_match_next():
+ 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.1 json"))
+ expected = {"192.168.255.1": {"bgpState": "Established"}}
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_has_routes(router, metric, weight):
+ output = json.loads(
+ router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 routes json")
+ )
+ expected = {
+ "routes": {"10.100.100.1/32": [{"metric": metric, "weight": weight}]}
+ }
+ return topotest.json_cmp(output, expected)
+
+ # Check thst session is established
+ test_func = functools.partial(_bgp_converge, router2)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed bgp convergence on r2"
+
+ # Check that metric is 0 and weight is 100 for the received prefix
+ test_func = functools.partial(_bgp_has_routes, router2, 0, 100)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "r2 does not receive routes with metric 0 and weight 100"
+
+ # Update the route-map and add "on-match next" to entry 10
+ cmd = """
+ configure terminal
+ route-map RM permit 10
+ on-match next
+ exit
+ """
+ router2.vtysh_cmd(cmd)
+
+ # Check that metric is 20 and weight is 100 for the received prefix
+ test_func = functools.partial(_bgp_has_routes, router2, 20, 100)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "r2 does not receive routes with metric 20 and weight 100"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_route_map_vpn_import/__init__.py b/tests/topotests/bgp_route_map_vpn_import/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_route_map_vpn_import/__init__.py
diff --git a/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf b/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf
new file mode 100644
index 0000000..4aa11ec
--- /dev/null
+++ b/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf
@@ -0,0 +1,46 @@
+!
+!debug bgp updates
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp nht
+!debug route-map
+!
+router bgp 65001
+ bgp router-id 10.10.10.10
+ no bgp ebgp-requires-policy
+!
+router bgp 65001 vrf Customer
+ bgp router-id 192.168.1.2
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ rd vpn export 192.168.1.2:2
+ rt vpn import 192.168.1.2:2
+ rt vpn export 192.168.1.2:2
+ export vpn
+ import vpn
+ exit-address-family
+!
+router bgp 65001 vrf Service
+ bgp router-id 192.168.2.2
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ 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
+ route-map vpn import from-customer
+ export vpn
+ import vpn
+ exit-address-family
+!
+bgp extcommunity-list standard from-customer seq 5 permit rt 192.168.1.2:2
+!
+ip prefix-list p1 seq 5 permit 192.0.2.0/24
+!
+route-map from-customer permit 10
+ match extcommunity from-customer
+ match ip address prefix-list p1
+ set local-preference 123
+route-map from-customer permit 20
+!
diff --git a/tests/topotests/bgp_route_map_vpn_import/r1/zebra.conf b/tests/topotests/bgp_route_map_vpn_import/r1/zebra.conf
new file mode 100644
index 0000000..51966b2
--- /dev/null
+++ b/tests/topotests/bgp_route_map_vpn_import/r1/zebra.conf
@@ -0,0 +1,16 @@
+!
+interface lo
+ ip address 10.10.10.10/32
+!
+interface r1-eth0 vrf Customer
+ ip address 192.168.1.2/24
+!
+interface r1-eth1 vrf Service
+ ip address 192.168.2.2/24
+!
+interface r1-eth2
+ ip address 10.0.1.1/24
+!
+interface r1-eth3 vrf Customer
+ ip address 192.0.2.1/24
+!
diff --git a/tests/topotests/bgp_route_map_vpn_import/test_bgp_route_map_vpn_import.py b/tests/topotests/bgp_route_map_vpn_import/test_bgp_route_map_vpn_import.py
new file mode 100644
index 0000000..37082b4
--- /dev/null
+++ b/tests/topotests/bgp_route_map_vpn_import/test_bgp_route_map_vpn_import.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if `route-map vpn import NAME` works by setting/matching via route-maps.
+Routes from VRF Customer to VRF Service MUST be leaked and modified later
+with `route-map vpn import`.
+"""
+
+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")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r1"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r1"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r1"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ r1 = tgen.gears["r1"]
+
+ r1.run("ip link add Customer type vrf table 1001")
+ r1.run("ip link set up dev Customer")
+ r1.run("ip link set r1-eth0 master Customer")
+ r1.run("ip link add Service type vrf table 1002")
+ r1.run("ip link set up dev Service")
+ r1.run("ip link set r1-eth1 master Service")
+ r1.run("ip link set r1-eth3 master Customer")
+
+ 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_vpn_import():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ def _bgp_check_received_leaked_with_vpn_import():
+ output = json.loads(r1.vtysh_cmd("show bgp vrf Service ipv4 unicast json"))
+ expected = {
+ "routes": {
+ "192.0.2.0/24": [
+ {
+ "locPrf": 123,
+ },
+ ],
+ "192.168.1.0/24": [
+ {
+ "locPrf": None,
+ }
+ ],
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_received_leaked_with_vpn_import)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Failed, imported routes are not modified"
+
+
+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..23cf041
--- /dev/null
+++ b/tests/topotests/bgp_route_server_client/test_bgp_route_server_client.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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..9984aba
--- /dev/null
+++ b/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_rr_ibgp_topo1.py
+#
+# Copyright (c) 2019 by
+# Cumulus Networks, Inc.
+# Donald Sharp
+#
+
+"""
+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..409be74
--- /dev/null
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf
@@ -0,0 +1,19 @@
+! 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
+ !
+!
+ip prefix-list p1 seq 5 permit 172.16.255.253/32
+!
+route-map prepend permit 10
+ match ip address prefix-list p1
+ set as-path prepend 65003
+!
+route-map prepend permit 20
+!
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..74489a0
--- /dev/null
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf
@@ -0,0 +1,10 @@
+! exit1
+interface lo
+ 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_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..dcb52a2
--- /dev/null
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf
@@ -0,0 +1,10 @@
+! 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 sender-as-path-loop-detection
+ neighbor 192.168.254.2 remote-as 65003
+ neighbor 192.168.254.2 timers 3 10
+ 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..519273d
--- /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..3886bc1
--- /dev/null
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_sender-as-path-loop-detection.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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)
+
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge():
+ output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
+ expected = {
+ "192.168.255.2": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_has_route_from_r1():
+ output = json.loads(r2.vtysh_cmd("show ip bgp 172.16.255.253/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_r1():
+ output = json.loads(
+ r2.vtysh_cmd("show ip bgp neighbor 192.168.255.2 advertised-routes json")
+ )
+ expected = {"totalPrefixCounter": 0}
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_suppress_route_to_r3():
+ output = json.loads(
+ r2.vtysh_cmd("show ip bgp neighbor 192.168.254.2 advertised-routes json")
+ )
+ expected = {"totalPrefixCounter": 2}
+ 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 bgp to convergence"
+
+ test_func = functools.partial(_bgp_has_route_from_r1)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed to see a route from r1"
+
+ test_func = functools.partial(_bgp_suppress_route_to_r3)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Route 172.16.255.253/32 should not be sent to r3 from r2"
+
+ test_func = functools.partial(_bgp_suppress_route_to_r1)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Routes should not be sent to r1 from r2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_set_aspath_exclude/__init__.py b/tests/topotests/bgp_set_aspath_exclude/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/__init__.py
diff --git a/tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf b/tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf
new file mode 100644
index 0000000..9bef24f
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/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 exclude 65003
+route-map r2 permit 20
+ set as-path exclude all
+!
diff --git a/tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf b/tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf
new file mode 100644
index 0000000..acf120b
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/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_exclude/r2/bgpd.conf b/tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf
new file mode 100644
index 0000000..23367f9
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/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_exclude/r2/zebra.conf b/tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf
new file mode 100644
index 0000000..f229954
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/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_exclude/r3/bgpd.conf b/tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf
new file mode 100644
index 0000000..b7a7ced
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/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_exclude/r3/zebra.conf b/tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf
new file mode 100644
index 0000000..3fa6c64
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/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_exclude/test_bgp_set_aspath_exclude.py b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py
new file mode 100644
index 0000000..a0cd89f
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# test_bgp_set_aspath_exclude.py
+#
+# Copyright 2023 by 6WIND S.A.
+#
+
+"""
+Test if `set as-path exclude` 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_set_aspath_exclude():
+ 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"}],
+ "172.16.255.32/32": [{"path": ""}],
+ }
+ }
+ 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"
+
+
+def test_bgp_set_aspath_exclude_access_list():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ rname = "r1"
+ r1 = tgen.gears[rname]
+
+ r1.vtysh_cmd(
+ """
+conf
+ bgp as-path access-list FIRST permit ^65
+ route-map r2 permit 6
+ set as-path exclude as-path-access-list FIRST
+ """
+ )
+
+ expected = {
+ "routes": {
+ "172.16.255.31/32": [{"path": ""}],
+ "172.16.255.32/32": [{"path": ""}],
+ }
+ }
+
+ def _bgp_regexp_1(router):
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
+
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_regexp_1, 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 regex 1 route-map"
+ r1.vtysh_cmd(
+ """
+conf
+ bgp as-path access-list SECOND permit 2
+ route-map r2 permit 6
+ set as-path exclude as-path-access-list SECOND
+ """
+ )
+
+ expected = {
+ "routes": {
+ "172.16.255.31/32": [{"path": "65003"}],
+ "172.16.255.32/32": [{"path": "65003"}],
+ }
+ }
+
+ test_func = functools.partial(_bgp_regexp_1, 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 regex 2 route-map"
+
+
+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..f586c1f
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf
@@ -0,0 +1,18 @@
+!
+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
+!
+bgp route-map delay-timer 1
+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..c0e19fa
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_set_aspath_replace.py
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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
+from lib.topolog import logger
+
+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_set_aspath_replace_test1():
+ 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"
+
+
+def test_bgp_set_aspath_replace_test2():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ logger.info("Configuring r1 to replace the matching AS with a configured ASN")
+ router = tgen.gears["r1"]
+ router.vtysh_cmd(
+ "configure terminal\nroute-map r2 permit 10\nset as-path replace 65003 65500\n",
+ isjson=False,
+ )
+ router.vtysh_cmd(
+ "configure terminal\nroute-map r2 permit 20\nset as-path replace any 65501\n",
+ isjson=False,
+ )
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
+ expected = {
+ "routes": {
+ "172.16.255.31/32": [{"path": "65002 65500"}],
+ "172.16.255.32/32": [{"path": "65501 65501"}],
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ _, 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 replace with configured ASN"
+
+
+def test_bgp_set_aspath_replace_access_list():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ rname = "r1"
+ r1 = tgen.gears[rname]
+
+ r1.vtysh_cmd(
+ """
+conf
+ bgp as-path access-list FIRST permit ^65
+ route-map r2 permit 20
+ set as-path replace as-path-access-list FIRST 65002
+ """
+ )
+
+ expected = {
+ "routes": {
+ "172.16.255.31/32": [{"path": "65002 65500"}],
+ "172.16.255.32/32": [{"path": "65002 65002"}],
+ }
+ }
+
+ def _bgp_regexp_1(router):
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
+
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_regexp_1, 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 regex 1 route-map"
+ r1.vtysh_cmd(
+ """
+conf
+ bgp as-path access-list SECOND permit 2
+ route-map r2 permit 10
+ set as-path replace as-path-access-list SECOND 65001
+ """
+ )
+
+ expected = {
+ "routes": {
+ "172.16.255.31/32": [{"path": "65001 65003"}],
+ "172.16.255.32/32": [{"path": "65002 65002"}],
+ }
+ }
+
+ test_func = functools.partial(_bgp_regexp_1, 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 regex 2 route-map"
+
+ r1.vtysh_cmd(
+ """
+conf
+ bgp as-path access-list TER permit 3
+ route-map r2 permit 10
+ set as-path replace as-path-access-list TER
+ """
+ )
+ expected = {
+ "routes": {
+ "172.16.255.31/32": [{"path": "65002 65001"}],
+ "172.16.255.32/32": [{"path": "65002 65002"}],
+ }
+ }
+
+ test_func = functools.partial(_bgp_regexp_1, 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 regex 3 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..292cf70
--- /dev/null
+++ b/tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# bgp_set_local-preference_add_subtract.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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_bgp4v2mib/r1/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf
new file mode 100644
index 0000000..d82a21e
--- /dev/null
+++ b/tests/topotests/bgp_snmp_bgp4v2mib/r1/bgpd.conf
@@ -0,0 +1,31 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ no bgp default ipv4-unicast
+ neighbor 192.168.12.2 remote-as external
+ neighbor 192.168.12.2 timers 1 3
+ neighbor 192.168.12.2 timers connect 1
+ neighbor 2001:db8::12:2 remote-as external
+ neighbor 2001:db8::12:2 timers 1 3
+ neighbor 2001:db8::12:2 timers connect 1
+ !
+ address-family ipv4 unicast
+ network 10.0.0.0/31 route-map p1
+ network 10.0.0.2/32 route-map p2
+ neighbor 192.168.12.2 activate
+ exit-address-family
+ address-family ipv6 unicast
+ network 2001:db8::1/128 route-map p1
+ network 2001:db8:1::/56 route-map p2
+ neighbor 2001:db8::12:2 activate
+ exit-address-family
+!
+route-map p1 permit 10
+ set metric 1
+exit
+route-map p2 permit 10
+ set metric 2
+ set origin incomplete
+exit
+!
diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r1/zebra.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r1/zebra.conf
new file mode 100644
index 0000000..0807e89
--- /dev/null
+++ b/tests/topotests/bgp_snmp_bgp4v2mib/r1/zebra.conf
@@ -0,0 +1,5 @@
+!
+interface r1-eth0
+ ip address 192.168.12.1/24
+ ipv6 address 2001:db8::12:1/64
+!
diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf
new file mode 100644
index 0000000..cf0013e
--- /dev/null
+++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf
@@ -0,0 +1,23 @@
+!
+!debug bgp updates
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ no bgp default ipv4-unicast
+ neighbor 192.168.12.1 remote-as external
+ neighbor 192.168.12.1 timers 1 3
+ neighbor 192.168.12.1 timers connect 1
+ neighbor 2001:db8::12:1 remote-as external
+ neighbor 2001:db8::12:1 timers 1 3
+ neighbor 2001:db8::12:1 timers connect 1
+ !
+ address-family ipv4 unicast
+ neighbor 192.168.12.1 activate
+ exit-address-family
+ address-family ipv6 unicast
+ neighbor 2001:db8::12:1 activate
+ exit-address-family
+!
+agentx
+!
diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf
new file mode 100644
index 0000000..032b93b
--- /dev/null
+++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/snmpd.conf
@@ -0,0 +1,17 @@
+agentAddress 127.0.0.1,[::1]
+
+group public_group v1 public
+group public_group v2c public
+access public_group "" any noauth prefix all all none
+
+rocommunity public default
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr
diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/zebra.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/zebra.conf
new file mode 100644
index 0000000..db6d700
--- /dev/null
+++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/zebra.conf
@@ -0,0 +1,5 @@
+!
+interface r2-eth0
+ ip address 192.168.12.2/24
+ ipv6 address 2001:db8::12:2/64
+!
diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py b/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py
new file mode 100755
index 0000000..a253d84
--- /dev/null
+++ b/tests/topotests/bgp_snmp_bgp4v2mib/test_bgp_snmp_bgp4v2mib.py
@@ -0,0 +1,236 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test some of the BGP4V2-MIB entries.
+"""
+
+import os
+import sys
+import json
+from time import sleep
+import pytest
+import functools
+
+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
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.snmp]
+
+
+def build_topo(tgen):
+ 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):
+ snmpd = os.system("which snmpd")
+ if snmpd:
+ error_msg = "SNMP not installed - skipping"
+ pytest.skip(error_msg)
+
+ 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)),
+ "-M snmp",
+ )
+ router.load_config(
+ TopoRouter.RD_SNMP,
+ os.path.join(CWD, "{}/snmpd.conf".format(rname)),
+ "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX",
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_snmp_bgp4v2():
+ tgen = get_topogen()
+
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge_summary():
+ output = json.loads(r2.vtysh_cmd("show bgp summary json"))
+ expected = {
+ "ipv4Unicast": {
+ "peers": {
+ "192.168.12.1": {
+ "state": "Established",
+ "pfxRcd": 2,
+ }
+ }
+ },
+ "ipv6Unicast": {
+ "peers": {
+ "2001:db8::12:1": {
+ "state": "Established",
+ "pfxRcd": 2,
+ }
+ }
+ },
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge_summary)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Can't see connections established"
+
+ def _bgp_converge_prefixes():
+ output = json.loads(r2.vtysh_cmd("show bgp all json"))
+ expected = {
+ "ipv4Unicast": {
+ "routes": {
+ "10.0.0.0/31": [
+ {
+ "metric": 1,
+ "origin": "IGP",
+ }
+ ],
+ "10.0.0.2/32": [
+ {
+ "metric": 2,
+ "origin": "incomplete",
+ }
+ ],
+ }
+ },
+ "ipv6Unicast": {
+ "routes": {
+ "2001:db8::1/128": [
+ {
+ "metric": 1,
+ "origin": "IGP",
+ }
+ ],
+ "2001:db8:1::/56": [
+ {
+ "metric": 2,
+ "origin": "incomplete",
+ }
+ ],
+ }
+ },
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge_prefixes)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Can't see prefixes from R1"
+
+ snmp = SnmpTester(r2, "localhost", "public", "2c", "-Ln -On")
+
+ def _snmpwalk_remote_addr():
+ expected = {
+ "1.3.6.1.3.5.1.1.2.1.5.1.1.192.168.12.1": "C0 A8 0C 01",
+ "1.3.6.1.3.5.1.1.2.1.5.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1": "20 01 0D B8 00 00 00 00 00 00 00 00 00 12 00 01",
+ }
+
+ # bgp4V2PeerRemoteAddr
+ output, _ = snmp.walk(".1.3.6.1.3.5.1.1.2.1.5")
+ return output == expected
+
+ _, result = topotest.run_and_expect(_snmpwalk_remote_addr, True, count=10, wait=1)
+ assertmsg = "Can't fetch SNMP for bgp4V2PeerRemoteAddr"
+ assert result, assertmsg
+
+ def _snmpwalk_peer_state():
+ expected = {
+ "1.3.6.1.3.5.1.1.2.1.13.1.1.192.168.12.1": "6",
+ "1.3.6.1.3.5.1.1.2.1.13.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1": "6",
+ }
+
+ # bgp4V2PeerState
+ output, _ = snmp.walk(".1.3.6.1.3.5.1.1.2.1.13")
+ return output == expected
+
+ _, result = topotest.run_and_expect(_snmpwalk_peer_state, True, count=10, wait=1)
+ assertmsg = "Can't fetch SNMP for bgp4V2PeerState"
+ assert result, assertmsg
+
+ def _snmpwalk_peer_last_error_code_received():
+ expected = {
+ "1.3.6.1.3.5.1.1.3.1.1.1.1.192.168.12.1": "0",
+ "1.3.6.1.3.5.1.1.3.1.1.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1": "0",
+ }
+
+ # bgp4V2PeerLastErrorCodeReceived
+ output, _ = snmp.walk(".1.3.6.1.3.5.1.1.3.1.1")
+ return output == expected
+
+ _, result = topotest.run_and_expect(
+ _snmpwalk_peer_last_error_code_received, True, count=10, wait=1
+ )
+ assertmsg = "Can't fetch SNMP for bgp4V2PeerLastErrorCodeReceived"
+ assert result, assertmsg
+
+ def _snmpwalk_origin():
+ expected = {
+ "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.0.31.1.192.168.12.1.1": "1",
+ "1.3.6.1.3.5.1.1.9.1.9.1.1.1.1.10.0.0.2.32.1.192.168.12.1.1": "3",
+ "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "1",
+ "1.3.6.1.3.5.1.1.9.1.9.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "3",
+ }
+
+ # bgp4V2NlriOrigin
+ output, _ = snmp.walk(".1.3.6.1.3.5.1.1.9.1.9")
+ return output == expected
+
+ _, result = topotest.run_and_expect(_snmpwalk_origin, True, count=10, wait=1)
+ assertmsg = "Can't fetch SNMP for bgp4V2NlriOrigin"
+ assert result, assertmsg
+
+ def _snmpwalk_med():
+ expected = {
+ "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.0.31.1.192.168.12.1.1": "1",
+ "1.3.6.1.3.5.1.1.9.1.17.1.1.1.1.10.0.0.2.32.1.192.168.12.1.1": "2",
+ "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.0.0.0.0.0.0.0.0.0.0.1.128.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "1",
+ "1.3.6.1.3.5.1.1.9.1.17.1.2.1.2.32.1.13.184.0.1.0.0.0.0.0.0.0.0.0.0.56.2.32.1.13.184.0.0.0.0.0.0.0.0.0.18.0.1.1": "2",
+ }
+
+ # bgp4V2NlriMed
+ output, _ = snmp.walk(".1.3.6.1.3.5.1.1.9.1.17")
+ # tgen.mininet_cli()
+ return output == expected
+
+ _, result = topotest.run_and_expect(_snmpwalk_med, True, count=10, wait=1)
+ assertmsg = "Can't fetch SNMP for bgp4V2NlriMed"
+ 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/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..c8d0bab
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce1/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp: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
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..c8d0bab
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce2/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp: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
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..c8d0bab
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce3/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp: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
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..c8d0bab
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce4/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp: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
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..435abde
--- /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 10
+ 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 10
+ 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 10
+ 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..d7886e5
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r1/snmpd.conf
@@ -0,0 +1,20 @@
+agentAddress udp: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
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..be5e7f5
--- /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 10
+ 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 10
+ 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..c8d0bab
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r2/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp: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
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..6f3d8ec
--- /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 10
+ 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 10
+ 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 10
+ 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..c8d0bab
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r3/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp: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
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..7d923b6
--- /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 10
+ 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 10
+ 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..c8d0bab
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r4/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp: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
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..0131e12
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py
@@ -0,0 +1,738 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_snmp_mplsl3vpn.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+"""
+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_software_version/__init__.py b/tests/topotests/bgp_software_version/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_software_version/__init__.py
diff --git a/tests/topotests/bgp_software_version/r1/bgpd.conf b/tests/topotests/bgp_software_version/r1/bgpd.conf
new file mode 100644
index 0000000..42d15f7
--- /dev/null
+++ b/tests/topotests/bgp_software_version/r1/bgpd.conf
@@ -0,0 +1,8 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ bgp default software-version-capability
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers 1 3
+ neighbor 192.168.1.2 timers connect 1
+!
diff --git a/tests/topotests/bgp_software_version/r1/zebra.conf b/tests/topotests/bgp_software_version/r1/zebra.conf
new file mode 100644
index 0000000..b29940f
--- /dev/null
+++ b/tests/topotests/bgp_software_version/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_software_version/r2/bgpd.conf b/tests/topotests/bgp_software_version/r2/bgpd.conf
new file mode 100644
index 0000000..c8ab017
--- /dev/null
+++ b/tests/topotests/bgp_software_version/r2/bgpd.conf
@@ -0,0 +1,7 @@
+router bgp 65002
+ 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
+ neighbor 192.168.1.1 capability software-version
+!
diff --git a/tests/topotests/bgp_software_version/r2/zebra.conf b/tests/topotests/bgp_software_version/r2/zebra.conf
new file mode 100644
index 0000000..cffe827
--- /dev/null
+++ b/tests/topotests/bgp_software_version/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_software_version/test_bgp_software_version.py b/tests/topotests/bgp_software_version/test_bgp_software_version.py
new file mode 100644
index 0000000..25e646c
--- /dev/null
+++ b/tests/topotests/bgp_software_version/test_bgp_software_version.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if Software Version capability works if forced with a knob.
+Reference: https://datatracker.ietf.org/doc/html/draft-abraitis-bgp-version-capability
+"""
+
+import os
+import re
+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_software_version():
+ 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": {"peers": {"192.168.1.2": {"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_software_version():
+ output = json.loads(r1.vtysh_cmd("show bgp neighbor 192.168.1.2 json"))
+
+ try:
+ versions = output["192.168.1.2"]["neighborCapabilities"]["softwareVersion"]
+ adv = versions["advertisedSoftwareVersion"]
+ rcv = versions["receivedSoftwareVersion"]
+
+ if not adv and not rcv:
+ return False
+
+ pattern = "^FRRouting/\\d.+"
+ if re.search(pattern, adv) and re.search(pattern, rcv):
+ return True
+ except:
+ return False
+
+ return False
+
+ assert _bgp_check_software_version(), "Neighbor's software version is n/a"
+
+
+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..967bed0
--- /dev/null
+++ b/tests/topotests/bgp_soo/test_bgp_soo.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+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/c31/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/bgpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/bgpd.conf
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/staticd.conf
new file mode 100644
index 0000000..0c88575
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/staticd.conf
@@ -0,0 +1,4 @@
+!
+ip route 0.0.0.0/0 192.168.3.254
+ipv6 route ::/0 2001:3::ffff
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/zebra.conf
new file mode 100644
index 0000000..3f75641
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c31/zebra.conf
@@ -0,0 +1,6 @@
+hostname c31
+!
+interface eth0
+ ip address 192.168.3.1/24
+ ipv6 address 2001:3::1/64
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/bgpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/bgpd.conf
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/staticd.conf
new file mode 100644
index 0000000..0c88575
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/staticd.conf
@@ -0,0 +1,4 @@
+!
+ip route 0.0.0.0/0 192.168.3.254
+ipv6 route ::/0 2001:3::ffff
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/zebra.conf
new file mode 100644
index 0000000..c06a7d1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c32/zebra.conf
@@ -0,0 +1,6 @@
+hostname c32
+!
+interface eth0
+ ip address 192.168.3.1/24
+ ipv6 address 2001:3::1/64
+!
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..22b9014
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf
@@ -0,0 +1,57 @@
+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:12::2 remote-as 65002
+ neighbor 2001:db8:12::2 timers 3 10
+ neighbor 2001:db8:12::2 timers connect 1
+ neighbor 2001:db8:12::2 capability extended-nexthop
+ neighbor 2001:db8:13::3 remote-as 65001
+ neighbor 2001:db8:13::3 timers 3 10
+ neighbor 2001:db8:13::3 timers connect 1
+ neighbor 2001:db8:13::3 capability extended-nexthop
+ !
+ segment-routing srv6
+ locator default
+ !
+ address-family ipv4 vpn
+ neighbor 2001:db8:12::2 activate
+ neighbor 2001:db8:13::3 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..49b64fd
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/staticd.conf
@@ -0,0 +1,4 @@
+!
+ipv6 route 2001:db8:2:2::/64 2001:db8:12::2
+ipv6 route 2001:db8:3:3::/64 2001:db8:13::3
+!
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..79dbf95
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/zebra.conf
@@ -0,0 +1,32 @@
+log file zebra.log
+!
+hostname r1
+!
+interface lo
+ ipv6 address 2001:db8:1:1::1/128
+!
+interface eth0
+ ipv6 address 2001:db8:12::1/64
+!
+interface eth1
+ ipv6 address 2001:db8:13::1/64
+!
+interface eth2 vrf vrf10
+ ip address 192.168.1.254/24
+!
+interface eth3 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..42b9d51
--- /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:12::1 remote-as 65001
+ neighbor 2001:db8:12::1 timers 3 10
+ neighbor 2001:db8:12::1 timers connect 1
+ neighbor 2001:db8:12::1 capability extended-nexthop
+ !
+ segment-routing srv6
+ locator default
+ !
+ address-family ipv4 vpn
+ neighbor 2001:db8:12::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..8d80c1e
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/staticd.conf
@@ -0,0 +1,4 @@
+!
+ipv6 route 2001:db8:1:1::/64 2001:db8:12::1
+ipv6 route 2001:db8:3:3::/64 2001:db8:12::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..09a65b9
--- /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:12::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/r3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf
new file mode 100644
index 0000000..339b4eb
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/bgpd.conf
@@ -0,0 +1,52 @@
+frr defaults traditional
+!
+hostname r2
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+router bgp 65001
+ bgp router-id 192.0.2.3
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001:db8:13::1 remote-as 65001
+ neighbor 2001:db8:13::1 timers 3 10
+ neighbor 2001:db8:13::1 timers connect 1
+ neighbor 2001:db8:13::1 capability extended-nexthop
+ !
+ segment-routing srv6
+ locator default
+ !
+ address-family ipv4 vpn
+ neighbor 2001:db8:13::1 activate
+ exit-address-family
+ !
+!
+router bgp 65001 vrf vrf10
+ bgp router-id 192.0.2.3
+ !
+ 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.2
+ !
+ 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/r3/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/staticd.conf
new file mode 100644
index 0000000..9d672d5
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/staticd.conf
@@ -0,0 +1,6 @@
+!
+ipv6 route 2001:db8:12::/64 2001:db8:13::1
+!
+ipv6 route 2001:db8:1:1::/64 2001:db8:13::1
+ipv6 route 2001:db8:2:2::/64 2001:db8:13::1
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/zebra.conf
new file mode 100644
index 0000000..a20cb76
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r3/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname r2
+!
+interface lo
+ ipv6 address 2001:db8:3:3::1/128
+!
+interface eth0
+ ipv6 address 2001:db8:13::3/64
+!
+interface eth1 vrf vrf10
+ ip address 192.168.3.254/24
+!
+interface eth2 vrf vrf20
+ ip address 192.168.3.254/24
+!
+segment-routing
+ srv6
+ locators
+ locator default
+ prefix 2001:db8:3:3::/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..14b9ba8
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/test_bgp_srv6l3vpn_over_ipv6.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+
+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
+from lib.checkping import check_ping
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+ tgen.add_router("r3")
+
+ tgen.add_router("c11")
+ tgen.add_router("c12")
+ tgen.add_router("c21")
+ tgen.add_router("c22")
+ tgen.add_router("c31")
+ tgen.add_router("c32")
+
+ tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0")
+ tgen.add_link(tgen.gears["r1"], tgen.gears["r3"], "eth1", "eth0")
+ tgen.add_link(tgen.gears["r1"], tgen.gears["c11"], "eth2", "eth0")
+ tgen.add_link(tgen.gears["r1"], tgen.gears["c12"], "eth3", "eth0")
+ tgen.add_link(tgen.gears["r2"], tgen.gears["c21"], "eth1", "eth0")
+ tgen.add_link(tgen.gears["r2"], tgen.gears["c22"], "eth2", "eth0")
+ tgen.add_link(tgen.gears["r3"], tgen.gears["c31"], "eth1", "eth0")
+ tgen.add_link(tgen.gears["r3"], tgen.gears["c32"], "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 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["r3"].run("sysctl net.vrf.strict_mode=1")
+ tgen.gears["r3"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["r3"].run("ip link set vrf10 up")
+ tgen.gears["r3"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["r3"].run("ip link set vrf20 up")
+ tgen.gears["r3"].run("ip link set eth1 master vrf10")
+ tgen.gears["r3"].run("ip link set eth2 master vrf20")
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_ping():
+ tgen = get_topogen()
+
+ check_ping("c11", "192.168.2.1", True, 10, 1)
+ check_ping("c11", "192.168.3.1", True, 10, 1)
+ check_ping("c12", "192.168.2.1", True, 10, 1)
+ check_ping("c12", "192.168.3.1", True, 10, 1)
+ check_ping("c21", "192.168.3.1", True, 10, 1)
+ check_ping("c22", "192.168.3.1", True, 10, 1)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/ce1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_route_leak/ce1/bgpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_route_leak/ce1/bgpd.conf
diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/ce1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_route_leak/ce1/zebra.conf
new file mode 100644
index 0000000..823a56d
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_route_leak/ce1/zebra.conf
@@ -0,0 +1,9 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface eth0
+ ip address 172.16.0.1/24
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/bgpd.conf
new file mode 100644
index 0000000..15779aa
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_route_leak/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
+ !
+ segment-routing srv6
+ locator default
+ exit
+ !
+!
+router bgp 65001 vrf vrf10
+ bgp router-id 192.0.2.1
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ sid vpn export auto
+ 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_srv6l3vpn_route_leak/pe1/results/default_ipv4_vpn.json b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/default_ipv4_vpn.json
new file mode 100644
index 0000000..dc86d7c
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/default_ipv4_vpn.json
@@ -0,0 +1,31 @@
+{
+ "vrfName": "default",
+ "routerId": "192.0.2.1",
+ "localAS": 65001,
+ "routes": {
+ "routeDistinguishers": {
+ "65001:10": {
+ "172.16.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "172.16.0.0",
+ "prefixLen": 24,
+ "network": "172.16.0.0\/24",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "hostname": "pe1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf10_ipv4_unicast.json b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf10_ipv4_unicast.json
new file mode 100644
index 0000000..ce2d5c1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf10_ipv4_unicast.json
@@ -0,0 +1,25 @@
+{
+ "vrfName": "vrf10",
+ "routerId": "192.0.2.1",
+ "localAS": 65001,
+ "routes": {
+ "172.16.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "172.16.0.0",
+ "prefixLen": 24,
+ "network": "172.16.0.0\/24",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "hostname": "pe1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json
new file mode 100644
index 0000000..9f78447
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json
@@ -0,0 +1,22 @@
+{
+ "172.16.0.0\/24": [
+ {
+ "prefix": "172.16.0.0\/24",
+ "prefixLen": 24,
+ "protocol": "bgp",
+ "vrfName": "vrf20",
+ "selected": true,
+ "destSelected": true,
+ "installed": true,
+ "nexthops": [
+ {
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "vrf": "vrf10",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4_unicast.json b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4_unicast.json
new file mode 100644
index 0000000..6a88d39
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4_unicast.json
@@ -0,0 +1,27 @@
+{
+ "vrfName": "vrf20",
+ "routerId": "192.0.2.1",
+ "localAS": 65001,
+ "routes": {
+ "172.16.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "172.16.0.0",
+ "prefixLen": 24,
+ "network": "172.16.0.0\/24",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "hostname": "pe1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/zebra.conf
new file mode 100644
index 0000000..52341fc
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/zebra.conf
@@ -0,0 +1,27 @@
+log file zebra.log
+!
+hostname pe1
+!
+interface lo
+ ip address 10.0.0.1/32
+!
+interface eth0 vrf vrf10
+ ip address 172.16.0.254/24
+!
+line vty
+!
+segment-routing
+ srv6
+ locators
+ locator default
+ prefix 2001:db8:2::/64 block-len 40 node-len 24 func-bits 16
+ exit
+ !
+ exit
+ !
+ exit
+ !
+exit
+!
+end
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/test_bgp_srv6l3vpn_route_leak.py b/tests/topotests/bgp_srv6l3vpn_route_leak/test_bgp_srv6l3vpn_route_leak.py
new file mode 100755
index 0000000..900d0c2
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_route_leak/test_bgp_srv6l3vpn_route_leak.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2022, LINE Corporation
+# Authored by Ryoga Saito <ryoga.saito@linecorp.com>
+#
+
+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(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 check_vrf10_bgp_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_bgp_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_bgp_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_vrf20_rib(output):
+ expected = open_json_file("%s/pe1/results/vrf20_ipv4.json" % CWD)
+ actual = json.loads(output)
+ return topotest.json_cmp(actual, expected)
+
+
+def test_rib():
+ check("pe1", "show bgp vrf vrf10 ipv4 unicast json", check_vrf10_bgp_rib)
+ check("pe1", "show bgp ipv4 vpn json", check_default_bgp_vpn_rib)
+ check("pe1", "show bgp vrf vrf20 ipv4 unicast json", check_vrf20_bgp_rib)
+ check("pe1", "show ip route vrf vrf20 json", check_vrf20_rib)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf
new file mode 100644
index 0000000..3459796
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce1/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json
new file mode 100644
index 0000000..d19e315
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf
new file mode 100644
index 0000000..bb5f93f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface eth0
+ ipv6 address 2001:1::2/64
+ ip address 192.168.1.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:1::1
+ip route 0.0.0.0/0 192.168.1.1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf
new file mode 100644
index 0000000..8ed9978
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce2/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json
new file mode 100644
index 0000000..35ff14e
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf
new file mode 100644
index 0000000..a52b83f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname ce2
+!
+interface eth0
+ ipv6 address 2001:2::2/64
+ ip address 192.168.2.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:2::1
+ip route 0.0.0.0/0 192.168.2.1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf
new file mode 100644
index 0000000..a85d970
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce3/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json
new file mode 100644
index 0000000..2f2931f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce3/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf
new file mode 100644
index 0000000..beca0b1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce4/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf
new file mode 100644
index 0000000..93fb32f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce4/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json
new file mode 100644
index 0000000..8a98768
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce4/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf
new file mode 100644
index 0000000..7b21074
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce5/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf
new file mode 100644
index 0000000..2ab6f2d
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce5/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json
new file mode 100644
index 0000000..80ff52a
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce5/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf
new file mode 100644
index 0000000..b5ad48e
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce6/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf
new file mode 100644
index 0000000..e0b6540
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce6/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json
new file mode 100644
index 0000000..ace6136
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/ce6/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf
new file mode 100644
index 0000000..7d19d98
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/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_sid/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf
new file mode 100644
index 0000000..bfc9db9
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf
@@ -0,0 +1,79 @@
+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 192.0.2.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 update-source 2001::1
+ neighbor 2001::2 capability extended-nexthop
+ !
+ address-family ipv4 vpn
+ neighbor 2001::2 activate
+ exit-address-family
+ !
+ address-family ipv6 vpn
+ neighbor 2001::2 activate
+ exit-address-family
+ !
+ segment-routing srv6
+ locator loc1
+ !
+!
+router bgp 1 vrf vrf10
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ !
+ 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
+!
+ address-family ipv4 unicast
+ network 192.168.1.0/24
+ sid vpn export auto
+ rd vpn export 11:10
+ rt vpn both 77:77
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
+router bgp 1 vrf vrf20
+ bgp router-id 192.0.2.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_sid/r1/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json
new file mode 100644
index 0000000..6fc43e1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json
@@ -0,0 +1,169 @@
+{
+ "vrfName": "default",
+ "tableVersion": 2,
+ "routerId": "192.0.2.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_sid/r1/vrf10_afv4_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json
new file mode 100644
index 0000000..9783c7e
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json
@@ -0,0 +1,23 @@
+{
+ "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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json
new file mode 100644
index 0000000..80c1acf
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json
@@ -0,0 +1,53 @@
+{
+ "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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "labels":[
+ 16
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:1::"
+ }
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json
new file mode 100644
index 0000000..9783c7e
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json
@@ -0,0 +1,23 @@
+{
+ "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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json
new file mode 100644
index 0000000..07ca64b
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json
@@ -0,0 +1,54 @@
+{
+ "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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"2001::2",
+ "afi":"ipv6",
+ "labels":[
+ 128
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:8::"
+ }
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json
new file mode 100644
index 0000000..6ac8dac
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json
@@ -0,0 +1,77 @@
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json
new file mode 100644
index 0000000..fac3d1d
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json
@@ -0,0 +1,107 @@
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:2::\/64":[
+ {
+ "prefix":"2001:2::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "labels":[
+ 32
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:2::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json
new file mode 100644
index 0000000..69ce312
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json
@@ -0,0 +1,77 @@
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json
new file mode 100644
index 0000000..04e2305
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json
@@ -0,0 +1,106 @@
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:2::\/64":[
+ {
+ "prefix":"2001:2::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "labels":[
+ 128
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:8::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json
new file mode 100644
index 0000000..3cac156
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json
@@ -0,0 +1,54 @@
+{
+ "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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"2001::2",
+ "afi":"ipv6",
+ "labels":[
+ 16
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:1::"
+ }
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json
new file mode 100644
index 0000000..163e9d6
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json
@@ -0,0 +1,53 @@
+{
+ "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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"2001::2",
+ "afi":"ipv6",
+ "labels":[
+ 128
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:8::"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json
new file mode 100644
index 0000000..1313f20
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json
@@ -0,0 +1,106 @@
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:2::\/64":[
+ {
+ "prefix":"2001:2::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "labels":[
+ 16
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:1::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json
new file mode 100644
index 0000000..51f249b
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json
@@ -0,0 +1,106 @@
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:2::\/64":[
+ {
+ "prefix":"2001:2::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "labels":[
+ 128
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:8::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json
new file mode 100644
index 0000000..6ac8dac
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json
@@ -0,0 +1,77 @@
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json
new file mode 100644
index 0000000..1c3dad0
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json
@@ -0,0 +1,22 @@
+{
+ "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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json
new file mode 100644
index 0000000..9579bb1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json
@@ -0,0 +1,112 @@
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "vrf":"default",
+ "active":true,
+ "labels":[
+ 32
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:2::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json
new file mode 100644
index 0000000..25f146f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json
@@ -0,0 +1,106 @@
+{
+ "2001:4::\/64":[
+ {
+ "prefix":"2001:4::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf20",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "vrf":"default",
+ "active":true,
+ "labels":[
+ 48
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:3::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:5::\/64":[
+ {
+ "prefix":"2001:5::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf20",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "vrf":"default",
+ "active":true,
+ "labels":[
+ 48
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:3::"
+ }
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf20",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf
new file mode 100644
index 0000000..cf31a5c
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf
@@ -0,0 +1,43 @@
+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
+ ip address 192.168.1.1/24
+!
+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 block-len 40 node-len 24 func-bits 16
+ !
+ !
+!
+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_sid/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf
new file mode 100644
index 0000000..892a9f7
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf
@@ -0,0 +1,80 @@
+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 192.0.2.2
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001::1 remote-as 1
+ neighbor 2001::1 update-source 2001::2
+ 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
+ !
+ address-family ipv6 vpn
+ neighbor 2001::1 activate
+ exit-address-family
+ !
+ segment-routing srv6
+ locator loc1
+ !
+!
+router bgp 2 vrf vrf10
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ !
+ 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
+!
+ address-family ipv4 unicast
+ network 192.168.2.0/24
+ sid vpn export auto
+ rd vpn export 22:10
+ rt vpn both 77:77
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
+router bgp 2 vrf vrf20
+ bgp router-id 192.0.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_sid/r2/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json
new file mode 100644
index 0000000..538e895
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json
@@ -0,0 +1,169 @@
+{
+ "vrfName": "default",
+ "tableVersion": 2,
+ "routerId": "192.0.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_sid/r2/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json
new file mode 100644
index 0000000..446bb8e
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json
@@ -0,0 +1,106 @@
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "vrf":"default",
+ "active":true,
+ "labels":[
+ 32
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:1:1:2::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:2::\/64":[
+ {
+ "prefix":"2001:2::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "vrf":"default",
+ "active":true,
+ "labels":[
+ 32
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:1:1:2::"
+ }
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json
new file mode 100644
index 0000000..8bc2fc2
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json
@@ -0,0 +1,112 @@
+{
+ "2001:4::\/64":[
+ {
+ "prefix":"2001:4::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf20",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "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,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "vrf":"default",
+ "active":true,
+ "labels":[
+ 48
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:1:1:3::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:6::\/64":[
+ {
+ "prefix":"2001:6::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf20",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf20",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf20",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf
new file mode 100644
index 0000000..9771ee1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf
@@ -0,0 +1,43 @@
+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
+ ip address 192.168.2.1/24
+!
+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 block-len 40 node-len 24 func-bits 16
+ !
+ !
+!
+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_sid/test_bgp_srv6l3vpn_sid.py b/tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py
new file mode 100755
index 0000000..984cf97
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py
@@ -0,0 +1,438 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright 2023 6WIND S.A.
+# Authored by Dmytro Shytyi <dmytro.shytyi@6wind.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
+from lib.checkping import check_ping
+
+
+def build_topo(tgen):
+ r"""
+ CE1 CE3 CE5
+ (eth0) (eth0) (eth0)
+ :2 :2 :2
+ | | |
+ 192.168.1.0 | |
+ /24 | |
+ 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 /
+ /192.168.2.0| / \ /
+ \ /24 / \ | | |
+ +------+ +------+ +------+
+ | | |
+ :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("5.11")
+ 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 i, (rname, router) in enumerate(tgen.routers().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.gears["r1"].run("modprobe vrf")
+ 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["r1"].run("sysctl net.vrf.strict_mode=1")
+ tgen.gears["r1"].run("sysctl net.ipv4.conf.default.rp_filter=0")
+ tgen.gears["r1"].run("sysctl net.ipv4.conf.all.rp_filter=0")
+ tgen.gears["r1"].run("sysctl net.ipv4.conf.lo.rp_filter=0")
+ tgen.gears["r1"].run("sysctl net.ipv4.conf.eth0.rp_filter=0")
+ tgen.gears["r1"].run("sysctl net.ipv4.conf.eth1.rp_filter=0")
+ tgen.gears["r1"].run("sysctl net.ipv4.conf.vrf10.rp_filter=0")
+
+ tgen.gears["r2"].run("modprobe vrf")
+ 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.gears["r2"].run("sysctl net.vrf.strict_mode=1")
+ tgen.gears["r2"].run("sysctl net.ipv4.conf.default.rp_filter=0")
+ tgen.gears["r2"].run("sysctl net.ipv4.conf.all.rp_filter=0")
+ tgen.gears["r2"].run("sysctl net.ipv4.conf.lo.rp_filter=0")
+ tgen.gears["r2"].run("sysctl net.ipv4.conf.eth0.rp_filter=0")
+ tgen.gears["r2"].run("sysctl net.ipv4.conf.eth1.rp_filter=0")
+ tgen.gears["r2"].run("sysctl net.ipv4.conf.vrf10.rp_filter=0")
+ 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.
+ # Example:
+ # tgen=get_topogen()
+ # tgen.mininet_cli()
+
+
+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_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, 10, 0.5)
+ check_ping("ce1", "2001:3::2", True, 10, 0.5)
+ check_ping("ce1", "2001:4::2", False, 10, 0.5)
+ check_ping("ce1", "2001:5::2", False, 10, 0.5)
+ check_ping("ce1", "2001:6::2", False, 10, 0.5)
+ check_ping("ce4", "2001:1::2", False, 10, 0.5)
+ check_ping("ce4", "2001:2::2", False, 10, 0.5)
+ check_ping("ce4", "2001:3::2", False, 10, 0.5)
+ check_ping("ce4", "2001:5::2", True, 10, 0.5)
+ check_ping("ce4", "2001:6::2", True, 10, 0.5)
+
+
+def test_sid_per_afv6_auto():
+ check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_sid_rib.json")
+ check_ping("ce1", "2001:2::2", True, 10, 0.5)
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv6 unicast
+ no sid vpn export auto
+ """
+ )
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_no_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", False, 10, 0.5)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv6 unicast
+ sid vpn export auto
+ """
+ )
+ check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_sid_rib.json")
+ check_ping("ce1", "2001:2::2", True, 10, 0.5)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv6 unicast
+ no sid vpn export auto
+ """
+ )
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_no_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", False, 10, 0.5)
+
+
+def test_sid_per_afv6_manual():
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_no_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", False, 10, 0.5)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv6 unicast
+ sid vpn export 8
+ """
+ )
+
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", True, 10, 0.5)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv6 unicast
+ no sid vpn export 8
+ """
+ )
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_no_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", False, 10, 0.5)
+
+
+def test_sid_per_afv4_auto():
+ check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_sid_rib.json")
+ check_ping("ce1", "192.168.2.2", True, 10, 0.5)
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv4 unicast
+ no sid vpn export auto
+ """
+ )
+
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_no_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", False, 10, 0.5)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv4 unicast
+ sid vpn export auto
+ """
+ )
+
+ check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_sid_rib.json")
+ check_ping("ce1", "192.168.2.2", True, 10, 0.5)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv4 unicast
+ no sid vpn export auto
+ """
+ )
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_no_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", False, 10, 0.5)
+
+
+def test_sid_per_afv4_manual():
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_no_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", False, 10, 0.5)
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv4 unicast
+ sid vpn export 8
+ """
+ )
+
+ check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_sid_rib.json")
+ check_ping("ce1", "192.168.2.2", True, 10, 0.5)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv4 unicast
+ no sid vpn export 8
+ """
+ )
+ check_ping("ce1", "192.168.2.2", False, 10, 0.5)
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_no_sid_rib.json"
+ )
+
+
+def test_sid_per_vrf_auto():
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf_auto_no_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", False, 10, 0.5)
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ sid vpn per-vrf export auto
+ """
+ )
+
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf6_auto_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", True, 10, 0.5)
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf4_auto_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", True, 10, 0.5)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ no sid vpn per-vrf export auto
+ """
+ )
+
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf_auto_no_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", False, 10, 0.5)
+
+
+def test_sid_per_vrf_manual():
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf_manual_no_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", False, 10, 0.5)
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ sid vpn per-vrf export 8
+ """
+ )
+
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf6_manual_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", True, 10, 0.5)
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf4_manual_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", True, 10, 0.5)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ no sid vpn per-vrf export 8
+ """
+ )
+
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf_manual_no_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", False, 10, 0.5)
+
+
+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..8ccf7a2
--- /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 func-bits 8
+ !
+ !
+!
+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..839454d
--- /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 func-bits 8
+ !
+ !
+!
+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..4afaeaf
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py
@@ -0,0 +1,276 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+
+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
+from lib.checkping import check_ping
+
+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_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, 10, 1)
+ check_ping("ce1", "2001:3::2", True, 10, 1)
+ check_ping("ce1", "2001:4::2", False, 10, 1)
+ check_ping("ce1", "2001:5::2", False, 10, 1)
+ check_ping("ce1", "2001:6::2", False, 10, 1)
+ check_ping("ce4", "2001:1::2", False, 10, 1)
+ check_ping("ce4", "2001:2::2", False, 10, 1)
+ check_ping("ce4", "2001:3::2", False, 10, 1)
+ check_ping("ce4", "2001:5::2", True, 10, 1)
+ check_ping("ce4", "2001:6::2", True, 10, 1)
+
+
+def test_locator_delete():
+ check_ping("ce1", "2001:2::2", True, 10, 1)
+ 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, 10, 1)
+
+
+def test_locator_recreate():
+ check_ping("ce1", "2001:2::2", False, 10, 1)
+ 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, 10, 1)
+
+
+def test_bgp_locator_unset():
+ check_ping("ce1", "2001:2::2", True, 10, 1)
+ 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, 10, 1)
+
+
+def test_bgp_locator_reset():
+ check_ping("ce1", "2001:2::2", False, 10, 1)
+ 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, 10, 1)
+
+
+def test_bgp_srv6_unset():
+ check_ping("ce1", "2001:2::2", True, 10, 1)
+ 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, 10, 1)
+
+
+def test_bgp_srv6_reset():
+ check_ping("ce1", "2001:2::2", False, 10, 1)
+ 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, 10, 1)
+
+
+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..30a0f8f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf
@@ -0,0 +1,68 @@
+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
+ 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..cbc5ce1
--- /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 func-bits 8
+ !
+ !
+!
+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..7ca2300
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf
@@ -0,0 +1,68 @@
+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
+ 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..449ca74
--- /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 func-bits 8
+ !
+ !
+!
+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..914c29f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/test_bgp_srv6l3vpn_to_bgp_vrf2.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+
+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
+from lib.checkping import check_ping
+
+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_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", True, 10, 0.5)
+ check_ping("ce1", "192.168.3.2", True, 10, 0.5)
+ check_ping("ce1", "192.168.4.2", False, 10, 0.5)
+ check_ping("ce1", "192.168.5.2", False, 10, 0.5)
+ check_ping("ce1", "192.168.6.2", False, 10, 0.5)
+ check_ping("ce4", "192.168.1.2", False, 10, 0.5)
+ check_ping("ce4", "192.168.2.2", False, 10, 0.5)
+ check_ping("ce4", "192.168.3.2", False, 10, 0.5)
+ check_ping("ce4", "192.168.5.2", True, 10, 0.5)
+ check_ping("ce4", "192.168.6.2", True, 10, 0.5)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/bgpd.conf
new file mode 100644
index 0000000..3459796
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce1/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/ip_rib.json
new file mode 100644
index 0000000..1d33fee
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce1/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/ipv6_rib.json
new file mode 100644
index 0000000..d19e315
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/zebra.conf
new file mode 100644
index 0000000..58e851d
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce1/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface eth0
+ ip address 192.168.1.2/24
+ ipv6 address 2001:1::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ip route 0.0.0.0/0 192.168.1.1
+ipv6 route ::/0 2001:1::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/bgpd.conf
new file mode 100644
index 0000000..8ed9978
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce2/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/ip_rib.json
new file mode 100644
index 0000000..a21f4a1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce2/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/ipv6_rib.json
new file mode 100644
index 0000000..35ff14e
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/zebra.conf
new file mode 100644
index 0000000..0612c53
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce2/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname ce2
+!
+interface eth0
+ ip address 192.168.2.2/24
+ ipv6 address 2001:2::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ip route 0.0.0.0/0 192.168.2.1
+ipv6 route ::/0 2001:2::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/bgpd.conf
new file mode 100644
index 0000000..a85d970
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce3/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/ip_rib.json
new file mode 100644
index 0000000..38a7807
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce3/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/ipv6_rib.json
new file mode 100644
index 0000000..2f2931f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce3/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/zebra.conf
new file mode 100644
index 0000000..d08fdad
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce3/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname ce3
+!
+interface eth0
+ ip address 192.168.3.2/24
+ ipv6 address 2001:3::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ip route 0.0.0.0/0 192.168.3.1
+ipv6 route ::/0 2001:3::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/bgpd.conf
new file mode 100644
index 0000000..93fb32f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce4/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/ip_rib.json
new file mode 100644
index 0000000..a0be78e
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce4/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/ipv6_rib.json
new file mode 100644
index 0000000..8a98768
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce4/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/zebra.conf
new file mode 100644
index 0000000..897ed46
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce4/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname ce4
+!
+interface eth0
+ ip address 192.168.4.2/24
+ ipv6 address 2001:4::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ip route 0.0.0.0/0 192.168.4.1
+ipv6 route ::/0 2001:4::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/bgpd.conf
new file mode 100644
index 0000000..2ab6f2d
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce5/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/ip_rib.json
new file mode 100644
index 0000000..dc338d5
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce5/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/ipv6_rib.json
new file mode 100644
index 0000000..80ff52a
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce5/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/zebra.conf
new file mode 100644
index 0000000..a290213
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce5/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname ce5
+!
+interface eth0
+ ip address 192.168.5.2/24
+ ipv6 address 2001:5::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ip route 0.0.0.0/0 192.168.5.1
+ipv6 route ::/0 2001:5::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/bgpd.conf
new file mode 100644
index 0000000..e0b6540
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce6/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/ip_rib.json
new file mode 100644
index 0000000..4a603a5
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce6/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/ipv6_rib.json
new file mode 100644
index 0000000..ace6136
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/ce6/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/zebra.conf
new file mode 100644
index 0000000..5a83e90
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/ce6/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname ce6
+!
+interface eth0
+ ip address 192.168.6.2/24
+ ipv6 address 2001:6::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ip route 0.0.0.0/0 192.168.6.1
+ipv6 route ::/0 2001:6::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf
new file mode 100644
index 0000000..01b0cc3
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/bgpd.conf
@@ -0,0 +1,87 @@
+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
+ neighbor 2001::2 capability extended-nexthop
+ !
+ address-family ipv4 vpn
+ neighbor 2001::2 activate
+ exit-address-family
+ !
+ 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
+ sid vpn per-vrf export auto
+ !
+ address-family ipv4 unicast
+ nexthop vpn export 2001::1
+ rd vpn export 1:10
+ rt vpn both 99:99
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ 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
+ sid vpn per-vrf export auto
+ !
+ address-family ipv4 unicast
+ nexthop vpn export 2001::1
+ rd vpn export 1:20
+ rt vpn both 88:88
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ 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_vrf3/r1/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib.json
new file mode 100644
index 0000000..3cc2fdd
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/r1/vpnv4_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_deleted.json
new file mode 100644
index 0000000..5645540
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_deleted.json
@@ -0,0 +1,90 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "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
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_recreated.json
new file mode 100644
index 0000000..7a4e0d7
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_locator_recreated.json
@@ -0,0 +1,166 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "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_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json
new file mode 100644
index 0000000..eb34333
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_disabled.json
@@ -0,0 +1,115 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 4,
+ "routerId": "1.1.1.1",
+ "defaultLocPrf": 100,
+ "localAS": 1,
+ "routes": {
+ "routeDistinguishers": {
+ "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_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json
new file mode 100644
index 0000000..5517fc7
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv4_rib_sid_vpn_export_reenabled.json
@@ -0,0 +1,167 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 6,
+ "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_vrf3/r1/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib.json
new file mode 100644
index 0000000..25b7a86
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/r1/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_locator_deleted.json
new file mode 100644
index 0000000..f2df9be
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/r1/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_locator_recreated.json
new file mode 100644
index 0000000..0fdd3d6
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json
new file mode 100644
index 0000000..a1f2158
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_disabled.json
@@ -0,0 +1,116 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 4,
+ "routerId": "1.1.1.1",
+ "defaultLocPrf": 100,
+ "localAS": 1,
+ "routes": {
+ "routeDistinguishers": {
+ "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_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json
new file mode 100644
index 0000000..7eeccd1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vpnv6_rib_sid_vpn_export_reenabled.json
@@ -0,0 +1,170 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 6,
+ "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_vrf3/r1/vrf10v4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v4_rib.json
new file mode 100644
index 0000000..cc96f43
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v4_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:1::"
+ }
+ }
+ ],
+ "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_vrf3/r1/vrf10v6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v6_rib.json
new file mode 100644
index 0000000..0c9ae73
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf10v6_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:1::"
+ }
+ }
+ ],
+ "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_vrf3/r1/vrf20v4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v4_rib.json
new file mode 100644
index 0000000..cf0fd18
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v4_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:2::"
+ }
+ }
+ ],
+ "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:2::"
+ }
+ }
+ ],
+ "asPath": "2"
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v6_rib.json
new file mode 100644
index 0000000..e486e74
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/vrf20v6_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:2::"
+ }
+ }
+ ],
+ "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:2::"
+ }
+ }
+ ],
+ "asPath": "2"
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf
new file mode 100644
index 0000000..f913b9f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf
@@ -0,0 +1,43 @@
+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
+ ipv6 address 2001:3::1/64
+!
+interface eth3 vrf vrf20
+ ip address 192.168.5.1/24
+ 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:2::/64 2001::2
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf
new file mode 100644
index 0000000..8700f12
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/bgpd.conf
@@ -0,0 +1,88 @@
+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
+ neighbor 2001::1 capability extended-nexthop
+ !
+ address-family ipv4 vpn
+ neighbor 2001::1 activate
+ exit-address-family
+ !
+ 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
+ sid vpn per-vrf export auto
+ !
+ address-family ipv4 unicast
+ nexthop vpn export 2001::2
+ rd vpn export 2:10
+ rt vpn both 99:99
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ 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
+ sid vpn per-vrf export auto
+ !
+ address-family ipv4 unicast
+ nexthop vpn export 2001::2
+ rd vpn export 2:20
+ rt vpn both 88:88
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ 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_vrf3/r2/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib.json
new file mode 100644
index 0000000..9557054
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/r2/vpnv4_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_deleted.json
new file mode 100644
index 0000000..e3edee1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_deleted.json
@@ -0,0 +1,90 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "routerId": "2.2.2.2",
+ "defaultLocPrf": 100,
+ "localAS": 2,
+ "routes": {
+ "routeDistinguishers": {
+ "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_vrf3/r2/vpnv4_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_recreated.json
new file mode 100644
index 0000000..0dcdec6
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_locator_recreated.json
@@ -0,0 +1,166 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "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_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json
new file mode 100644
index 0000000..d801671
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_disabled.json
@@ -0,0 +1,117 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 4,
+ "routerId": "2.2.2.2",
+ "defaultLocPrf": 100,
+ "localAS": 2,
+ "routes": {
+ "routeDistinguishers": {
+ "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_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json
new file mode 100644
index 0000000..25da05b
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv4_rib_sid_vpn_export_reenabled.json
@@ -0,0 +1,167 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 6,
+ "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_vrf3/r2/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib.json
new file mode 100644
index 0000000..2cd47b9
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/r2/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_locator_deleted.json
new file mode 100644
index 0000000..25cdf03
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/r2/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_locator_recreated.json
new file mode 100644
index 0000000..03bbcc0
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/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_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json
new file mode 100644
index 0000000..f390ef6
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_disabled.json
@@ -0,0 +1,120 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 4,
+ "routerId": "2.2.2.2",
+ "defaultLocPrf": 100,
+ "localAS": 2,
+ "routes": {
+ "routeDistinguishers": {
+ "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_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json
new file mode 100644
index 0000000..3353d75
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vpnv6_rib_sid_vpn_export_reenabled.json
@@ -0,0 +1,170 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 6,
+ "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_vrf3/r2/vrf10v4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v4_rib.json
new file mode 100644
index 0000000..1534e98
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v4_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:1::"
+ }
+ }
+ ],
+ "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:1::"
+ }
+ }
+ ],
+ "asPath": "1"
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v6_rib.json
new file mode 100644
index 0000000..a2e329d
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf10v6_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:1::"
+ }
+ }
+ ],
+ "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:1::"
+ }
+ }
+ ],
+ "asPath": "1"
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v4_rib.json
new file mode 100644
index 0000000..49d1861
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v4_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:2::"
+ }
+ }
+ ],
+ "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_vrf3/r2/vrf20v6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v6_rib.json
new file mode 100644
index 0000000..f7433d5
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/vrf20v6_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:2::"
+ }
+ }
+ ],
+ "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_vrf3/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf
new file mode 100644
index 0000000..201d0cc
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf
@@ -0,0 +1,43 @@
+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
+ ipv6 address 2001:2::1/64
+!
+interface eth2 vrf vrf20
+ ip address 192.168.4.1/24
+ ipv6 address 2001:4::1/64
+!
+interface eth3 vrf vrf20
+ ip address 192.168.6.1/24
+ 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
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/test_bgp_srv6l3vpn_to_bgp_vrf3.py b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/test_bgp_srv6l3vpn_to_bgp_vrf3.py
new file mode 100644
index 0000000..8a7b558
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/test_bgp_srv6l3vpn_to_bgp_vrf3.py
@@ -0,0 +1,337 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2022, University of Rome Tor Vergata
+# Authored by Carmine Scarpitta <carmine.scarpitta@uniroma2.it>
+#
+
+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
+from lib.checkping import check_ping, check_ping
+
+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.14")
+ 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_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/vrf10v4_rib.json")
+ check_rib("r1", "show ip route vrf vrf20 json", "r1/vrf20v4_rib.json")
+ check_rib("r2", "show ip route vrf vrf10 json", "r2/vrf10v4_rib.json")
+ check_rib("r2", "show ip route vrf vrf20 json", "r2/vrf20v4_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")
+
+ 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/vrf10v6_rib.json")
+ check_rib("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20v6_rib.json")
+ check_rib("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10v6_rib.json")
+ check_rib("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20v6_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", "192.168.2.2", True, 10, 0.5)
+ check_ping("ce1", "192.168.3.2", True, 10, 0.5)
+ check_ping("ce1", "192.168.4.2", False, 10, 0.5)
+ check_ping("ce1", "192.168.5.2", False, 10, 0.5)
+ check_ping("ce1", "192.168.6.2", False, 10, 0.5)
+ check_ping("ce4", "192.168.1.2", False, 10, 0.5)
+ check_ping("ce4", "192.168.2.2", False, 10, 0.5)
+ check_ping("ce4", "192.168.3.2", False, 10, 0.5)
+ check_ping("ce4", "192.168.5.2", True, 10, 0.5)
+ check_ping("ce4", "192.168.6.2", True, 10, 0.5)
+
+ check_ping("ce1", "2001:2::2", True, 10, 1)
+ check_ping("ce1", "2001:3::2", True, 10, 1)
+ check_ping("ce1", "2001:4::2", False, 10, 1)
+ check_ping("ce1", "2001:5::2", False, 10, 1)
+ check_ping("ce1", "2001:6::2", False, 10, 1)
+ check_ping("ce4", "2001:1::2", False, 10, 1)
+ check_ping("ce4", "2001:2::2", False, 10, 1)
+ check_ping("ce4", "2001:3::2", False, 10, 1)
+ check_ping("ce4", "2001:5::2", True, 10, 1)
+ check_ping("ce4", "2001:6::2", True, 10, 1)
+
+
+def test_bgp_sid_vpn_export_disable():
+ check_ping("ce1", "192.168.2.2", True, 10, 0.5)
+ check_ping("ce1", "2001:2::2", True, 10, 1)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1 vrf vrf10
+ segment-routing srv6
+ no sid vpn per-vrf export
+ """
+ )
+ check_rib(
+ "r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_sid_vpn_export_disabled.json"
+ )
+ check_rib(
+ "r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_sid_vpn_export_disabled.json"
+ )
+ check_rib(
+ "r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_sid_vpn_export_disabled.json"
+ )
+ check_rib(
+ "r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_sid_vpn_export_disabled.json"
+ )
+ check_ping("ce1", "192.168.2.2", False, 10, 0.5)
+ check_ping("ce1", "2001:2::2", False, 10, 1)
+
+
+def test_bgp_sid_vpn_export_reenable():
+ check_ping("ce1", "192.168.2.2", False, 10, 0.5)
+ check_ping("ce1", "2001:2::2", False, 10, 1)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1 vrf vrf10
+ segment-routing srv6
+ sid vpn per-vrf export auto
+ """
+ )
+ check_rib(
+ "r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_sid_vpn_export_reenabled.json"
+ )
+ check_rib(
+ "r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_sid_vpn_export_reenabled.json"
+ )
+ check_rib(
+ "r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_sid_vpn_export_reenabled.json"
+ )
+ check_rib(
+ "r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_sid_vpn_export_reenabled.json"
+ )
+ check_ping("ce1", "192.168.2.2", True, 10, 0.5)
+ check_ping("ce1", "2001:2::2", True, 10, 1)
+
+
+def test_locator_delete():
+ check_ping("ce1", "192.168.2.2", True, 10, 0.5)
+ check_ping("ce1", "2001:2::2", True, 10, 1)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ no locator loc1
+ """
+ )
+ check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_deleted.json")
+ check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_deleted.json")
+ 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", "192.168.2.2", False, 10, 0.5)
+ check_ping("ce1", "2001:2::2", False, 10, 1)
+
+
+def test_locator_recreate():
+ check_ping("ce1", "192.168.2.2", False, 10, 0.5)
+ check_ping("ce1", "2001:2::2", False, 10, 1)
+ 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 ipv4 vpn json", "r1/vpnv4_rib_locator_recreated.json")
+ check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_recreated.json")
+ 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", "192.168.2.2", True, 10, 0.5)
+ check_ping("ce1", "2001:2::2", True, 10, 1)
+
+
+def test_bgp_locator_unset():
+ check_ping("ce1", "192.168.2.2", True, 10, 0.5)
+ check_ping("ce1", "2001:2::2", True, 10, 1)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ segment-routing srv6
+ no locator loc1
+ """
+ )
+ check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_deleted.json")
+ check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_deleted.json")
+ 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", "192.168.2.2", False, 10, 0.5)
+ check_ping("ce1", "2001:2::2", False, 10, 1)
+
+
+def test_bgp_locator_reset():
+ check_ping("ce1", "192.168.2.2", False, 10, 0.5)
+ check_ping("ce1", "2001:2::2", False, 10, 1)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ segment-routing srv6
+ locator loc1
+ """
+ )
+ check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_recreated.json")
+ check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_recreated.json")
+ 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", "192.168.2.2", True, 10, 0.5)
+ check_ping("ce1", "2001:2::2", True, 10, 1)
+
+
+def test_bgp_srv6_unset():
+ check_ping("ce1", "192.168.2.2", True, 10, 0.5)
+ check_ping("ce1", "2001:2::2", True, 10, 1)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ no segment-routing srv6
+ """
+ )
+ check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_deleted.json")
+ check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_deleted.json")
+ 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", "192.168.2.2", False, 10, 0.5)
+ check_ping("ce1", "2001:2::2", False, 10, 1)
+
+
+def test_bgp_srv6_reset():
+ check_ping("ce1", "192.168.2.2", False, 10, 0.5)
+ check_ping("ce1", "2001:2::2", False, 10, 1)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ segment-routing srv6
+ locator loc1
+ """
+ )
+ check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib_locator_recreated.json")
+ check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib_locator_recreated.json")
+ 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", "192.168.2.2", True, 10, 0.5)
+ check_ping("ce1", "2001:2::2", True, 10, 1)
+
+
+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..fb6980a
--- /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..129b812
--- /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
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..fd8a78b
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
@@ -0,0 +1,237 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_suppress_fib.py
+#
+# Copyright (c) 2019 by
+#
+
+"""
+"""
+
+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=10, wait=0.5)
+ 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 60.0.0.0 json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert result is None, assertmsg
+
+
+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
+
+def test_local_vs_non_local():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+
+ output = json.loads(r2.vtysh_cmd("show bgp ipv4 uni 60.0.0.0/24 json"))
+ paths = output["paths"]
+ for i in range(len(paths)):
+ if "fibPending" in paths[i]:
+ assert(False), "Route 60.0.0.0/24 should not have fibPending"
+
+
+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..e7948ea
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# bgp_tcp_mss.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by
+# Abhinay Ramesh <rabhinay@vmware.com>
+#
+
+"""
+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=10, 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=10, 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..6fe044f
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss/test_bgp_vrf_tcp_mss.py
@@ -0,0 +1,753 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# bgp_tcp_mss.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by
+# Shreenidhi A R <rshreenidhi@vmware.com>
+#
+
+"""
+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_tcp_mss_passive/__init__.py b/tests/topotests/bgp_tcp_mss_passive/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss_passive/__init__.py
diff --git a/tests/topotests/bgp_tcp_mss_passive/r1/frr.conf b/tests/topotests/bgp_tcp_mss_passive/r1/frr.conf
new file mode 100644
index 0000000..a0fcd52
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss_passive/r1/frr.conf
@@ -0,0 +1,12 @@
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+router bgp 65001
+ 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.1.2 passive
+ neighbor 192.168.1.2 tcp-mss 300
+!
diff --git a/tests/topotests/bgp_tcp_mss_passive/r2/frr.conf b/tests/topotests/bgp_tcp_mss_passive/r2/frr.conf
new file mode 100644
index 0000000..7213975
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss_passive/r2/frr.conf
@@ -0,0 +1,10 @@
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+!
+router bgp 65002
+ 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
+!
diff --git a/tests/topotests/bgp_tcp_mss_passive/test_bgp_tcp_mss_passive.py b/tests/topotests/bgp_tcp_mss_passive/test_bgp_tcp_mss_passive.py
new file mode 100644
index 0000000..cd405f7
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss_passive/test_bgp_tcp_mss_passive.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if TCP MSS is synced with passive neighbor.
+"""
+
+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_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_tcp_mss_passive():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_check_tcp_mss_configured(router, neighbor, mss):
+ output = json.loads(router.vtysh_cmd("show bgp neighbors json"))
+ expected = {
+ neighbor: {
+ "bgpTcpMssConfigured": mss,
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_check_tcp_mss_configured, tgen.gears["r1"], "192.168.1.2", 300
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "r1 is not configured with TCP MSS 300"
+
+ test_func = functools.partial(
+ _bgp_check_tcp_mss_configured, tgen.gears["r2"], "192.168.1.1", 0
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "r2 is not configured with the default TCP MSS (1500)"
+
+ def _bgp_check_tcp_mss_synced(router, neighbor, mss):
+ output = json.loads(router.vtysh_cmd("show bgp neighbors json"))
+ expected = {
+ neighbor: {
+ "bgpTcpMssSynced": mss,
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_check_tcp_mss_synced, tgen.gears["r1"], "192.168.1.2", 288
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "r1 is not synced with TCP MSS 300"
+
+ test_func = functools.partial(
+ _bgp_check_tcp_mss_synced, tgen.gears["r2"], "192.168.1.1", 288
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "r2 is not synced with the default TCP MSS (1488)"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_unique_rid/bgp_unique_rid.json b/tests/topotests/bgp_unique_rid/bgp_unique_rid.json
new file mode 100644
index 0000000..c42ce29
--- /dev/null
+++ b/tests/topotests/bgp_unique_rid/bgp_unique_rid.json
@@ -0,0 +1,505 @@
+{
+ "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"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "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",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r5": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r4-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link7": {
+ "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": {},
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {},
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r4": {},
+ "r5": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "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": {
+ "r4": {},
+ "r4-link1": {},
+ "r4-link2": {},
+ "r4-link3": {},
+ "r4-link4": {},
+ "r4-link5": {},
+ "r4-link6": {},
+ "r4-link7": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {},
+ "r4-link1": {},
+ "r4-link2": {},
+ "r4-link3": {},
+ "r4-link4": {},
+ "r4-link5": {},
+ "r4-link6": {},
+ "r4-link7": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "10.10.10.10",
+ "neighbors": {
+ "r3": {}
+ }
+ }
+ },
+ "r5": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "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"
+ }
+ ]
+ }
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.5",
+ "neighbors": {
+ "r3": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_unique_rid/bgp_unique_rid_vrf.json b/tests/topotests/bgp_unique_rid/bgp_unique_rid_vrf.json
new file mode 100644
index 0000000..1e280f1
--- /dev/null
+++ b/tests/topotests/bgp_unique_rid/bgp_unique_rid_vrf.json
@@ -0,0 +1,529 @@
+{
+ "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": "GREEN"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "GREEN"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "GREEN"
+ }
+ },
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "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"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ],
+ "vrfs": [
+ {
+ "name": "GREEN",
+ "id": "1"
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback",
+ "vrf": "GREEN"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "GREEN"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "GREEN"
+ }
+ },
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "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"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ],
+ "vrfs": [
+ {
+ "name": "GREEN",
+ "id": "1"
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback",
+ "vrf": "GREEN"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "GREEN"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "GREEN"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r5": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ },
+ "r4-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": [
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {},
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {},
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "vrfs": [
+ {
+ "name": "RED",
+ "id": "1"
+ },
+ {
+ "name": "GREEN",
+ "id": "2"
+ }
+ ]
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "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": {
+ "r4": {},
+ "r4-link1": {},
+ "r4-link2": {},
+ "r4-link3": {},
+ "r4-link4": {},
+ "r4-link5": {},
+ "r4-link6": {},
+ "r4-link7": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {},
+ "r4-link1": {},
+ "r4-link2": {},
+ "r4-link3": {},
+ "r4-link4": {},
+ "r4-link5": {},
+ "r4-link6": {},
+ "r4-link7": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ }
+ }
+ }]
+ },
+ "r5": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback",
+ "vrf": "RED"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ }
+ },
+ "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"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ],
+ "vrfs": [
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ]
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py
new file mode 100644
index 0000000..f89f337
--- /dev/null
+++ b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py
@@ -0,0 +1,891 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+import sys
+import time
+import pytest
+import inspect
+import os
+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/"))
+
+"""Following tests are covered to test bgp unique rid functionality.
+1. Verify eBGP session when same and different router ID is configured.
+2. Verify iBGP session when same and different router ID is configured.
+3. Verify two different eBGP sessions initiated with same router ID.
+4. Chaos - Verify bgp unique rid functionality in chaos scenarios.
+5. Chaos - Verify bgp unique rid functionality when router reboots with same loopback id.
+6. Chaos - Verify bgp unique rid functionality when router reboots without any ip addresses.
+"""
+
+#################################
+# TOPOLOGY
+#################################
+"""
+
+ +-------+
+ +--------- | R2 |
+ | +-------+
+ |iBGP |
+ +-------+ |
+ | R1 | |iBGP
+ +-------+ |
+ | |
+ | iBGP +-------+ eBGP +-------+
+ +---------- | R3 |========= | R4 |
+ +-------+ +-------+
+ |
+ |eBGP
+ |
+ +-------+
+ | R5 |
+ +-------+
+
+
+"""
+
+# 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.ospfd, pytest.mark.staticd]
+
+# Required to instantiate the topology builder class.
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ step,
+ write_test_footer,
+ verify_rib,
+ check_address_types,
+ reset_config_on_routers,
+ check_router_status,
+ stop_router,
+ kill_router_daemons,
+ start_router_daemons,
+ start_router,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp_and_verify,
+)
+
+# Global variables
+topo = None
+bgp_convergence = False
+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",
+ ],
+}
+
+bgp_convergence = False
+ADDR_TYPES = check_address_types()
+routerid = {"ipv4": "10.10.10.14", "ipv6": "fd00:0:0:3::2"}
+
+
+def setup_module(mod):
+ """setup_module.
+
+ Set 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_unique_rid.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():
+ """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_bgp_unique_rid_ebgp_p0():
+ """
+ TC: 1
+ Verify eBGP session when same and different router ID is configured.
+ """
+ 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)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Base config should be up, verify using BGP convergence on all \
+ the routers for IPv4 and IPv6 nbrs"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure the same router id between R4 and R3 10.10.10.10")
+ input_dict = {
+ "r3": {"bgp": {"router_id": "10.10.10.10"}},
+ "r4": {"bgp": {"router_id": "10.10.10.10"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure the same router id between R5 and R3 (10.10.10.10)")
+ input_dict = {
+ "r3": {"bgp": {"router_id": "10.10.10.10"}},
+ "r5": {"bgp": {"router_id": "10.10.10.10"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("modify the router id on r3 to different router id (11.11.11.11)")
+ input_dict = {"r3": {"bgp": {"router_id": "11.11.11.11"}}}
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Reset bgp process")
+ step("Verify neighbours are in ESTAB state.")
+ dut = "r3"
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear ip bgp process with *")
+ step("Verify neighbours are in ESTAB state.")
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbours between R3 and R4 in EVPN address family.")
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {"unicast": {}},
+ "ipv6": {"unicast": {}},
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {"unicast": {}},
+ "ipv6": {"unicast": {}},
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_unique_rid_ibgp_p0():
+ """
+ TC: 2
+ Verify iBGP session when same and different router ID is configured.
+ """
+ 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)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Base config should be up, verify using BGP convergence on all \
+ the routers for IPv4 and IPv6 nbrs"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure the same router id between R1 and R3 (10.10.10.10)")
+ input_dict = {
+ "r3": {"bgp": {"router_id": "10.10.10.10"}},
+ "r1": {"bgp": {"router_id": "10.10.10.10"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in idle state.")
+ result = verify_bgp_convergence(tgen, topo, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure the same router id between R2 and R3 (10.10.10.10)")
+ input_dict = {
+ "r3": {"bgp": {"router_id": "10.10.10.10"}},
+ "r2": {"bgp": {"router_id": "10.10.10.10"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in idle state.")
+ result = verify_bgp_convergence(tgen, topo, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("modify the router id on r3 to different router id (11.11.11.11)")
+ input_dict = {"r3": {"bgp": {"router_id": "11.11.11.11"}}}
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = verify_bgp_convergence(tgen, topo, dut="r3")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Reset bgp process")
+ step("Verify neighbours are in ESTAB state.")
+ dut = "r3"
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear ip bgp process with *")
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_unique_rid_multi_bgp_nbrs_p0():
+ """
+ TC: 3
+ 3. Verify two different eBGP sessions initiated with same router ID
+
+ """
+ tgen = get_topogen()
+ global bgp_convergence, topo
+
+ 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)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Base config should be up, verify using BGP convergence on all \
+ the routers for IPv4 and IPv6 nbrs"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure the same router id between R3, R4 and R5 (10.10.10.10)")
+ input_dict = {
+ "r3": {"bgp": {"router_id": "10.10.10.10"}},
+ "r4": {"bgp": {"router_id": "10.10.10.10"}},
+ "r5": {"bgp": {"router_id": "10.10.10.10"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure the same IP address on on R4 and R5 loopback address and \
+ change the neighborship to loopback neighbours between R3 to R4 \
+ and R3 to R5 respectively."
+ )
+
+ topo1 = deepcopy(topo)
+
+ for rtr in ["r4", "r5"]:
+ topo1["routers"][rtr]["links"]["lo"]["ipv4"] = "192.168.1.1/32"
+
+ topo1["routers"]["r3"]["links"]["lo"]["ipv4"] = "192.168.1.3/32"
+ build_config_from_json(tgen, topo1, save_bkup=False)
+
+ step(
+ "change the neighborship to loopback neighbours between R3 to R4 and R3 to R5 respectively."
+ )
+ for rtr in ["r4", "r5"]:
+ configure_bgp_on_rtr = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"neighbor": {rtr: {"dest_link": {"lo": {}}}}}
+ }
+ }
+ },
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_rtr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3")
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Change the IP address on the R4 loopback.")
+ topo1["routers"]["r4"]["links"]["lo"]["ipv4"] = "192.168.1.4/32"
+ build_config_from_json(tgen, topo1, save_bkup=False)
+
+ step("Verify neighbours should be again in ESTAB state. (show ip bgp neighbours)")
+ bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3")
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Clear ip bgp process with *")
+ result = clear_bgp_and_verify(tgen, topo, router="r3")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_unique_rid_chaos1_p2():
+ """
+ TC: 4
+ 4. Chaos - Verify bgp unique rid functionality in chaos scenarios.
+
+ """
+ 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)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Base config should be up, verify using BGP convergence on all \
+ the routers for IPv4 and IPv6 nbrs"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure the same router id between R3, R4 and R5 (10.10.10.10)")
+ input_dict = {
+ "r3": {"bgp": {"router_id": "10.10.10.10"}},
+ "r4": {"bgp": {"router_id": "10.10.10.10"}},
+ "r5": {"bgp": {"router_id": "10.10.10.10"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify eBGP session when same router ID is configured and bgpd process is restarted"
+ )
+
+ # restart bgpd router and verify
+ kill_router_daemons(tgen, "r3", ["bgpd"])
+ start_router_daemons(tgen, "r3", ["bgpd"])
+
+ step(
+ "The session should be established between R3 & R4. "
+ "Once after restart bgp, neighbor should come back up ."
+ )
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step(
+ "Verify eBGP session when same router ID is configured and neighbor shutdown is issued and again no shutdown."
+ )
+
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3-link1": {"shutdown": True},
+ "r3-link2": {"shutdown": True},
+ "r3-link3": {"shutdown": True},
+ "r3-link4": {"shutdown": True},
+ "r3-link5": {"shutdown": True},
+ "r3-link6": {"shutdown": True},
+ "r3-link7": {"shutdown": True},
+ }
+ },
+ "r5": {"dest_link": {"r3": {"shutdown": True}}},
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3-link1": {"shutdown": True},
+ "r3-link2": {"shutdown": True},
+ "r3-link3": {"shutdown": True},
+ "r3-link4": {"shutdown": True},
+ "r3-link5": {"shutdown": True},
+ "r3-link6": {"shutdown": True},
+ "r3-link7": {"shutdown": True},
+ }
+ },
+ "r5": {"dest_link": {"r3": {"shutdown": True}}},
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3-link1": {"shutdown": False},
+ "r3-link2": {"shutdown": False},
+ "r3-link3": {"shutdown": False},
+ "r3-link4": {"shutdown": False},
+ "r3-link5": {"shutdown": False},
+ "r3-link6": {"shutdown": False},
+ "r3-link7": {"shutdown": False},
+ }
+ },
+ "r5": {"dest_link": {"r3": {"shutdown": False}}},
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3-link1": {"shutdown": False},
+ "r3-link2": {"shutdown": False},
+ "r3-link3": {"shutdown": False},
+ "r3-link4": {"shutdown": False},
+ "r3-link5": {"shutdown": False},
+ "r3-link6": {"shutdown": False},
+ "r3-link7": {"shutdown": False},
+ }
+ },
+ "r5": {"dest_link": {"r3": {"shutdown": False}}},
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "The session should be established between R3 & R4. "
+ "Once after restart bgp, neighbor should come back up ."
+ )
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step(
+ "Verify eBGP session when same router ID is configured and neighbor config is deleted & reconfigured."
+ )
+
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {},
+ }
+ },
+ "r5": {"dest_link": {"r3": {}}},
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {},
+ }
+ },
+ "r5": {"dest_link": {"r3": {}}},
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "The session should be established between R3 & R4. "
+ "Once after restart bgp, neighbor should come back up ."
+ )
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step(
+ "Verify eBGP session when same router ID is configured and FRR router is restarted."
+ )
+ stop_router(tgen, "r3")
+ start_router(tgen, "r3")
+
+ step(
+ "The session should be established between R3 & R4. "
+ "Once after restart bgp, neighbor should come back up ."
+ )
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step(
+ "Verify eBGP session when same router ID is configured and zebra process is restarted"
+ )
+
+ kill_router_daemons(tgen, "r3", ["zebra"])
+ start_router_daemons(tgen, "r3", ["zebra"])
+
+ step(
+ "The session should be established between R3 & R4. "
+ "Once after restart bgp, neighbor should come back up ."
+ )
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_unique_rid_chaos3_p2():
+ """
+ TC: 4
+ 4. Chaos - Verify bgp unique rid functionality when router reboots with same loopback id.
+
+ """
+ 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)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ global topo
+ topo1 = deepcopy(topo)
+
+ for rtr in topo["routers"].keys():
+ topo1["routers"][rtr]["links"]["lo"]["ipv4"] = "192.168.1.1/32"
+
+ topo1["routers"]["r3"]["links"]["lo"]["ipv4"] = "192.168.1.3/32"
+ 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
+ )
+
+ step(
+ "Configure loopback on R1 to R5 with IP address 1.1.1.1 on all the routers. Change neighborship on all the routers using loopback neighborship ids."
+ )
+ for rtr in ["r1", "r2", "r4", "r5"]:
+ configure_bgp_on_rtr = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"neighbor": {rtr: {"dest_link": {"lo": {}}}}}
+ }
+ }
+ },
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_rtr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3")
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Reboot the router (restart frr) or using watch frr.")
+ stop_router(tgen, "r3")
+ start_router(tgen, "r3")
+
+ step("Neighbors between R3, R4 and R3 to R5 should be in ESTB state.")
+ bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3")
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Clear bgp process.")
+ clear_bgp_and_verify(tgen, topo, "r3")
+
+ step("Neighbors between R3, R4 and R3 to R5 should be in ESTB state.")
+ bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3")
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_unique_rid_chaos4_p2():
+ """
+ TC: 6
+ 6. Chaos - Verify bgp unique rid functionality when router reboots without any ip addresses.
+
+ """
+ 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)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ reset_config_on_routers(tgen)
+
+ global topo
+ topo1 = deepcopy(topo)
+ topo2 = deepcopy(topo)
+
+ step(
+ "Configure base config as per the topology without loopback as well as Ip address on any of the interface."
+ )
+ for rtr in topo["routers"].keys():
+ for intf in topo["routers"][rtr]["links"].keys():
+ topo1["routers"][rtr]["links"][intf].pop("ipv4")
+ topo1["routers"][rtr]["links"][intf].pop("ipv6")
+
+ build_config_from_json(tgen, topo1, save_bkup=False)
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Configure the ip addresses on the physical interfaces")
+ build_config_from_json(tgen, topo2, save_bkup=False)
+
+ step("All the neighbors should be in ESTAB state.")
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Configure loopback addresses with higher IP address ")
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ step("All the neighbors should be in ESTAB state.")
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Reboot the router (restart frr) or using watch frr.")
+ stop_router(tgen, "r3")
+ start_router(tgen, "r3")
+
+ step("Neighbors between R3, R4 and R3 to R5 should be in ESTB state.")
+ bgp_convergence = verify_bgp_convergence(tgen, topo, dut="r3")
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_unique_rid/test_bgp_unique_rid_vrf.py b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid_vrf.py
new file mode 100644
index 0000000..2a9e42d
--- /dev/null
+++ b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid_vrf.py
@@ -0,0 +1,466 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+import sys
+import time
+import pytest
+import inspect
+import os
+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/"))
+
+"""Following tests are covered to test bgp unique rid functionality.
+1. Verify iBGP session when same and different router ID is configured in user VRF(GREEN).
+2. Verify eBGP session when same and different router ID is configured in user vrf (VRF RED)
+3. Verify two different eBGP sessions initiated with same router ID in user VRf (RED and GREEN)
+"""
+
+#################################
+# TOPOLOGY
+#################################
+"""
+
+ +-------+
+ +--------- | R2 |
+ | +-------+
+ |iBGP |
+ +-------+ |
+ | R1 | |iBGP
+ +-------+ |
+ | |
+ | iBGP +-------+ eBGP +-------+
+ +---------- | R3 |========= | R4 |
+ +-------+ +-------+
+ |
+ |eBGP
+ |
+ +-------+
+ | R5 |
+ +-------+
+
+
+"""
+
+# 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]
+
+# Required to instantiate the topology builder class.
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ step,
+ write_test_footer,
+ 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,
+ clear_bgp_and_verify,
+)
+
+# Global variables
+topo = None
+bgp_convergence = False
+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",
+ ],
+}
+
+bgp_convergence = False
+ADDR_TYPES = check_address_types()
+
+
+def setup_module(mod):
+ """setup_module.
+
+ Set 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_unique_rid_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)
+
+ # 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():
+ """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_bgp_unique_rid_ebgp_vrf_p0():
+ """
+ TC: 1
+ Verify iBGP session when same and different router ID is configured in user VRF(GREEN).
+ """
+ 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)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Base config should be up, verify using BGP convergence on all \
+ the routers for IPv4 and IPv6 nbrs"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure the same router id between R4 and R3 10.10.10.10")
+ input_dict = {
+ "r3": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}},
+ "r4": {"bgp": {"router_id": "10.10.10.10", "local_as": 200, "vrf": "RED"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure the same router id between R5 and R3 (10.10.10.10)")
+ input_dict = {
+ "r3": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}},
+ "r5": {"bgp": {"router_id": "10.10.10.10", "local_as": 300, "vrf": "RED"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("modify the router id on r3 to different router id (11.11.11.11)")
+ input_dict = {
+ "r3": {"bgp": {"router_id": "11.11.11.11", "local_as": 100, "vrf": "RED"}}
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Reset bgp process")
+ step("Verify neighbours are in ESTAB state.")
+ dut = "r3"
+ result = clear_bgp_and_verify(tgen, topo, router="r3")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear ip bgp process with *")
+ step("Verify neighbours are in ESTAB state.")
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbours between R3 and R4 in EVPN address family.")
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "local_as": 100,
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {"unicast": {}},
+ "ipv6": {"unicast": {}},
+ }
+ }
+ }
+ },
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": 200,
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {"unicast": {}},
+ "ipv6": {"unicast": {}},
+ }
+ }
+ }
+ },
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_unique_rid_ibgp_vrf_p0():
+ """
+ TC: 2
+ Verify eBGP session when same and different router ID is configured in user vrf (VRF RED)
+ """
+ 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)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Base config should be up, verify using BGP convergence on all \
+ the routers for IPv4 and IPv6 nbrs"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure the same router id between R1 and R3 (10.10.10.10)")
+ input_dict = {
+ "r3": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}},
+ "r1": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure the same router id between R2 and R3 (10.10.10.10)")
+ input_dict = {
+ "r3": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}},
+ "r2": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("modify the router id on r3 to different router id (11.11.11.11)")
+ input_dict = {
+ "r3": {"bgp": {"router_id": "11.11.11.11", "local_as": 100, "vrf": "RED"}}
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = verify_bgp_convergence(tgen, topo, dut="r3")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Reset bgp process")
+ step("Verify neighbours are in ESTAB state.")
+ dut = "r3"
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear ip bgp process with *")
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_unique_rid_multi_bgp_nbrs_vrf_p0():
+ """
+ TC: 3
+ Verify two different eBGP sessions initiated with same router ID in user VRf (RED and GREEN)
+
+ """
+ tgen = get_topogen()
+ global bgp_convergence, topo
+
+ 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)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Base config should be up, verify using BGP convergence on all \
+ the routers for IPv4 and IPv6 nbrs"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure the same router id between R3, R4 and R5 (10.10.10.10)")
+ input_dict = {
+ "r3": {"bgp": {"router_id": "10.10.10.10", "local_as": 100, "vrf": "RED"}},
+ "r4": {"bgp": {"router_id": "10.10.10.10", "local_as": 200, "vrf": "RED"}},
+ "r5": {"bgp": {"router_id": "10.10.10.10", "local_as": 300, "vrf": "RED"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify neighbours are in ESTAB state.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure the same IP address on on R4 and R5 loopback address and \
+ change the neighborship to loopback neighbours between R3 to R4 \
+ and R3 to R5 respectively."
+ )
+
+ topo1 = deepcopy(topo)
+
+ for rtr in ["r4", "r5"]:
+ topo1["routers"][rtr]["links"]["lo"]["ipv4"] = "192.168.1.1/32"
+
+ topo1["routers"]["r3"]["links"]["lo"]["ipv4"] = "192.168.1.3/32"
+ build_config_from_json(tgen, topo1, save_bkup=False)
+
+ step(
+ "change the neighborship to loopback neighbours between R3 to R4 and R3 to R5 respectively."
+ )
+ for rtr in ["r4", "r5"]:
+ configure_bgp_on_rtr = {
+ "r3": {
+ "bgp": {
+ "local_as": 100,
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"neighbor": {rtr: {"dest_link": {"lo": {}}}}}
+ }
+ },
+ },
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_rtr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3")
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Change the IP address on the R4 loopback.")
+ topo1["routers"]["r4"]["links"]["lo"]["ipv4"] = "192.168.1.4/32"
+ build_config_from_json(tgen, topo1, save_bkup=False)
+
+ step("Verify neighbours should be again in ESTAB state. (show ip bgp neighbours)")
+ bgp_convergence = verify_bgp_convergence(tgen, topo1, dut="r3")
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Clear ip bgp process with *")
+ result = clear_bgp_and_verify(tgen, topo, router="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/bgp_unnumbered/__init__.py b/tests/topotests/bgp_unnumbered/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_unnumbered/__init__.py
diff --git a/tests/topotests/bgp_unnumbered/r1/bgpd.conf b/tests/topotests/bgp_unnumbered/r1/bgpd.conf
new file mode 100644
index 0000000..a9d0ec8
--- /dev/null
+++ b/tests/topotests/bgp_unnumbered/r1/bgpd.conf
@@ -0,0 +1,9 @@
+!
+router bgp 65001
+ timers bgp 1 9
+ no bgp ebgp-requires-policy
+ neighbor r1-eth0 interface remote-as external
+ address-family ipv4 unicast
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_unnumbered/r1/zebra.conf b/tests/topotests/bgp_unnumbered/r1/zebra.conf
new file mode 100644
index 0000000..1cbaea4
--- /dev/null
+++ b/tests/topotests/bgp_unnumbered/r1/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface lo
+ ip address 172.16.250.254/32
+!
+interface r1-eth0
+ ip address 192.168.0.1/24
+!
+ip forwarding
+!
+
diff --git a/tests/topotests/bgp_unnumbered/r2/bgpd.conf b/tests/topotests/bgp_unnumbered/r2/bgpd.conf
new file mode 100644
index 0000000..fd29cd3
--- /dev/null
+++ b/tests/topotests/bgp_unnumbered/r2/bgpd.conf
@@ -0,0 +1,9 @@
+!
+router bgp 65002
+ no bgp network import-check
+ no bgp ebgp-requires-policy
+ timers bgp 1 9
+ neighbor r2-eth0 interface remote-as external
+ address-family ipv4 uni
+ network 172.16.255.254/32
+!
diff --git a/tests/topotests/bgp_unnumbered/r2/zebra.conf b/tests/topotests/bgp_unnumbered/r2/zebra.conf
new file mode 100644
index 0000000..cf6fb6d
--- /dev/null
+++ b/tests/topotests/bgp_unnumbered/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_unnumbered/test_bgp_unnumbered.py b/tests/topotests/bgp_unnumbered/test_bgp_unnumbered.py
new file mode 100644
index 0000000..2a53547
--- /dev/null
+++ b/tests/topotests/bgp_unnumbered/test_bgp_unnumbered.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2022 by
+# Donald Sharp
+#
+
+"""
+Test some bgp interface based issues that show up
+"""
+
+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")
+ 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):
+ 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()
+
+
+#
+# Test these events:
+# a) create an unnumbered neighbor
+# b) shutdown the interface
+# c) remove the unnumbered peer in bgp and bgp does not crash
+def test_bgp_unnumbered_removal():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_nexthop_cache():
+ output = tgen.gears["r1"].vtysh_cmd("show bgp nexthop")
+ expected = "Current BGP nexthop cache:\n"
+ return output == expected
+
+ def _bgp_converge():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd("show ip bgp 172.16.255.254/32 json")
+ )
+ expected = {"prefix": "172.16.255.254/32"}
+
+ return topotest.json_cmp(output, expected)
+
+ step("Ensure Convergence of BGP")
+ test_func = functools.partial(_bgp_converge)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+ assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r2"])
+
+ step("Shutdown interface r1-eth0")
+
+ tgen.gears["r1"].vtysh_cmd(
+ """
+ configure
+ int r1-eth0
+ shutdown
+ """
+ )
+
+ step("Remove the neighbor from r1")
+ tgen.gears["r1"].vtysh_cmd(
+ """
+ configure
+ router bgp
+ no neighbor r1-eth0 interface remote-as external
+ """
+ )
+
+ step("Ensure that BGP does not crash")
+ test_func = functools.partial(_bgp_nexthop_cache)
+ success, result = topotest.run_and_expect(test_func, True, count=10, wait=1)
+
+ assert result is True, "BGP did not crash on r1"
+
+
+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..1fcedaa
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/r2/zebra.conf
@@ -0,0 +1,16 @@
+! 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
+!
+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..4e66cf5
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/test_bgp_update_delay.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_update_delay.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Don Slice <dslice@nvidia.com>
+#
+
+"""
+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_vpn_5549_route_map/__init__.py b/tests/topotests/bgp_vpn_5549_route_map/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/__init__.py
diff --git a/tests/topotests/bgp_vpn_5549_route_map/cpe1/bgpd.conf b/tests/topotests/bgp_vpn_5549_route_map/cpe1/bgpd.conf
new file mode 100644
index 0000000..013cd8c
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/cpe1/bgpd.conf
@@ -0,0 +1,9 @@
+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
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/cpe1/zebra.conf b/tests/topotests/bgp_vpn_5549_route_map/cpe1/zebra.conf
new file mode 100644
index 0000000..49dcfc3
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/cpe1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface cpe1-eth0
+ ip address 192.168.1.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/cpe2/bgpd.conf b/tests/topotests/bgp_vpn_5549_route_map/cpe2/bgpd.conf
new file mode 100644
index 0000000..d65d507
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/cpe2/bgpd.conf
@@ -0,0 +1,6 @@
+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
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/cpe2/zebra.conf b/tests/topotests/bgp_vpn_5549_route_map/cpe2/zebra.conf
new file mode 100644
index 0000000..a47319e
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/cpe2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface cpe2-eth0
+ ip address 192.168.2.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe1/bgpd.conf b/tests/topotests/bgp_vpn_5549_route_map/pe1/bgpd.conf
new file mode 100644
index 0000000..93da025
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe1/bgpd.conf
@@ -0,0 +1,38 @@
+router bgp 65001
+ bgp router-id 10.10.10.10
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001:db8:1::2 remote-as internal
+ neighbor 2001:db8:1::2 update-source 2001:db8:1::1
+ neighbor 2001:db8:1::2 timers 1 3
+ neighbor 2001:db8:1::2 timers connect 1
+ neighbor 2001:db8:1::2 capability extended-nexthop
+ address-family ipv4 vpn
+ neighbor 2001:db8:1::2 activate
+ neighbor 2001:db8:1::2 route-map pe2 out
+ 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
+ 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
+!
+ip prefix-list cpe1 seq 5 permit 172.16.255.1/32
+!
+route-map pe2 permit 10
+ match ip address prefix-list cpe1
+ set ipv6 vpn next-hop 2001:db8::1
+!
+route-map pe2 permit 20
+exit
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe1/ldpd.conf b/tests/topotests/bgp_vpn_5549_route_map/pe1/ldpd.conf
new file mode 100644
index 0000000..fb40f06
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/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_vpn_5549_route_map/pe1/ospf6d.conf b/tests/topotests/bgp_vpn_5549_route_map/pe1/ospf6d.conf
new file mode 100644
index 0000000..0053d1e
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe1/ospf6d.conf
@@ -0,0 +1,12 @@
+!
+interface lo
+ ipv6 ospf6 area 0
+!
+interface pe1-eth1
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 dead-interval 3
+!
+router ospf6
+ ospf6 router-id 10.10.10.10
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe1/zebra.conf b/tests/topotests/bgp_vpn_5549_route_map/pe1/zebra.conf
new file mode 100644
index 0000000..da91055
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe1/zebra.conf
@@ -0,0 +1,14 @@
+!
+interface lo
+ ip address 10.10.10.10/32
+ ipv6 address 2001:db8:1::1/128
+!
+interface pe1-eth0 vrf RED
+ ip address 192.168.1.2/24
+!
+interface pe1-eth1
+ ip address 10.0.1.1/24
+ ipv6 address 2001:db8::1/64
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe2/bgpd.conf b/tests/topotests/bgp_vpn_5549_route_map/pe2/bgpd.conf
new file mode 100644
index 0000000..6db1eef
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe2/bgpd.conf
@@ -0,0 +1,29 @@
+router bgp 65001
+ bgp router-id 10.10.10.20
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001:db8:1::1 remote-as internal
+ neighbor 2001:db8:1::1 update-source 2001:db8:1::2
+ neighbor 2001:db8:1::1 timers 1 3
+ neighbor 2001:db8:1::1 timers connect 1
+ neighbor 2001:db8:1::1 capability extended-nexthop
+ address-family ipv4 vpn
+ neighbor 2001:db8:1::1 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
+ 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
+!
+
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe2/ldpd.conf b/tests/topotests/bgp_vpn_5549_route_map/pe2/ldpd.conf
new file mode 100644
index 0000000..e2b5359
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/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_vpn_5549_route_map/pe2/ospf6d.conf b/tests/topotests/bgp_vpn_5549_route_map/pe2/ospf6d.conf
new file mode 100644
index 0000000..f79bb4f
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe2/ospf6d.conf
@@ -0,0 +1,12 @@
+!
+interface lo
+ ipv6 ospf6 area 0
+!
+interface pe2-eth0
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 dead-interval 3
+!
+router ospf6
+ ospf6 router-id 10.10.10.20
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/pe2/zebra.conf b/tests/topotests/bgp_vpn_5549_route_map/pe2/zebra.conf
new file mode 100644
index 0000000..19ef7bf
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/pe2/zebra.conf
@@ -0,0 +1,14 @@
+!
+interface lo
+ ip address 10.10.10.20/32
+ ipv6 address 2001:db8:1::2/128
+!
+interface pe2-eth1 vrf RED
+ ip address 192.168.2.2/24
+!
+interface pe2-eth0
+ ip address 10.0.1.2/24
+ ipv6 address 2001:db8::2/64
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py b/tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py
new file mode 100644
index 0000000..eb29875
--- /dev/null
+++ b/tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py
@@ -0,0 +1,171 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Check if we can override VPN underlay next-hop from PE1 to PE2.
+"""
+
+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"])
+
+
+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_OSPF6, os.path.join(CWD, "{}/ospf6d.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_vpn_5549():
+ tgen = get_topogen()
+
+ pe2 = tgen.gears["pe2"]
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_vpn_nexthop_changed():
+ output = json.loads(pe2.vtysh_cmd("show bgp ipv4 vpn json"))
+ expected = {
+ "routes": {
+ "routeDistinguishers": {
+ "192.168.1.2:2": {
+ "172.16.255.1/32": [
+ {"valid": True, "nexthops": [{"ip": "2001:db8::1"}]}
+ ],
+ "192.168.1.0/24": [
+ {"valid": True, "nexthops": [{"ip": "2001:db8:1::1"}]}
+ ],
+ }
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_verify_v4_nexthop_validity():
+ output = json.loads(tgen.gears["cpe1"].vtysh_cmd("show bgp nexthop json"))
+ expected = {
+ "ipv4": {
+ "192.168.1.2": {
+ "valid": True,
+ "complete": True,
+ "igpMetric": 0,
+ "pathCount": 0,
+ "nexthops": [{"interfaceName": "cpe1-eth0"}],
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_verify_v6_global_nexthop_validity():
+ output = json.loads(tgen.gears["pe2"].vtysh_cmd("show bgp nexthop json"))
+ expected = {
+ "ipv6": {
+ "2001:db8::1": {
+ "valid": True,
+ "complete": True,
+ "igpMetric": 0,
+ "pathCount": 2,
+ "nexthops": [{"interfaceName": "pe2-eth0"}],
+ },
+ "2001:db8:1::1": {
+ "valid": True,
+ "complete": True,
+ "igpMetric": 10,
+ "pathCount": 2,
+ "peer": "2001:db8:1::1",
+ "nexthops": [{"interfaceName": "pe2-eth0"}],
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_vpn_nexthop_changed)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed overriding IPv6 next-hop for VPN underlay"
+
+ test_func = functools.partial(_bgp_verify_v4_nexthop_validity)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "IPv4 nexthop is invalid"
+
+ test_func = functools.partial(_bgp_verify_v6_global_nexthop_validity)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "IPv6 nexthop is invalid"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_vpnv4_asbr/__init__.py b/tests/topotests/bgp_vpnv4_asbr/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/__init__.py
diff --git a/tests/topotests/bgp_vpnv4_asbr/h1/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/h1/zebra.conf
new file mode 100644
index 0000000..2237224
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/h1/zebra.conf
@@ -0,0 +1,7 @@
+log stdout
+ip route 172.31.1.0/24 172.31.0.1
+ip route 172.31.2.0/24 172.31.0.1
+interface h1-eth0
+ ip address 172.31.0.10/24
+!
+
diff --git a/tests/topotests/bgp_vpnv4_asbr/h2/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/h2/zebra.conf
new file mode 100644
index 0000000..d650bc8
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/h2/zebra.conf
@@ -0,0 +1,6 @@
+log stdout
+ip route 172.31.0.0/24 172.31.1.1
+interface h2-eth0
+ ip address 172.31.1.10/24
+!
+
diff --git a/tests/topotests/bgp_vpnv4_asbr/h3/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/h3/zebra.conf
new file mode 100644
index 0000000..5676485
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/h3/zebra.conf
@@ -0,0 +1,6 @@
+log stdout
+ip route 172.31.0.0/24 172.31.2.1
+interface h3-eth0
+ ip address 172.31.2.10/24
+!
+
diff --git a/tests/topotests/bgp_vpnv4_asbr/r1/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_asbr/r1/bgp_ipv4_routes.json
new file mode 100644
index 0000000..184ab31
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/r1/bgp_ipv4_routes.json
@@ -0,0 +1,49 @@
+{
+ "vrfName": "vrf1",
+ "localAS": 65500,
+ "routes":
+ {
+ "172.31.0.10/32": [
+ {
+ "prefix": "172.31.0.10",
+ "prefixLen": 32,
+ "network": "172.31.0.10\/32",
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "192.168.0.3",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ },
+ {
+ "prefix": "172.31.0.10",
+ "prefixLen": 32,
+ "network": "172.31.0.10\/32",
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "192.168.0.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.31.0.1/32": [
+ {
+ "prefix": "172.31.0.1",
+ "prefixLen": 32,
+ "network": "172.31.0.1\/32",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf
new file mode 100644
index 0000000..3bbcc20
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf
@@ -0,0 +1,29 @@
+router bgp 65500
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ neighbor 192.0.2.100 remote-as 65500
+ neighbor 192.0.2.100 update-source lo
+ neighbor 192.168.0.100 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 192.168.0.100 activate
+ no neighbor 192.0.2.100 activate
+ network 192.0.2.1/32
+ exit-address-family
+ address-family ipv4 labeled-unicast
+ neighbor 192.168.0.100 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 192.0.2.100 activate
+ 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
+!
diff --git a/tests/topotests/bgp_vpnv4_asbr/r1/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/r1/zebra.conf
new file mode 100644
index 0000000..2f12b72
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/r1/zebra.conf
@@ -0,0 +1,10 @@
+log stdout
+interface lo
+ ip address 192.0.2.1/32
+!
+interface r1-eth1 vrf vrf1
+ ip address 172.31.0.1/24
+!
+interface r1-eth0
+ ip address 192.168.0.1/24
+!
diff --git a/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf
new file mode 100644
index 0000000..4c84d52
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf
@@ -0,0 +1,31 @@
+debug bgp nht
+debug bgp zebra
+debug bgp labelpool
+router bgp 65500
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ neighbor 192.0.2.100 remote-as 65500
+ neighbor 192.0.2.100 update-source lo
+ neighbor 192.168.0.100 remote-as 65500
+ neighbor 192.168.1.200 remote-as 65502
+ address-family ipv4 unicast
+ no neighbor 192.168.0.100 activate
+ no neighbor 192.168.1.200 activate
+ network 192.0.2.2/32
+ exit-address-family
+ address-family ipv4 labeled-unicast
+ neighbor 192.168.0.100 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 192.0.2.100 activate
+ neighbor 192.0.2.100 next-hop-self
+ neighbor 192.168.1.200 activate
+ exit-address-family
+!
+interface r2-eth1
+ mpls bgp forwarding
+ mpls bgp l3vpn-multi-domain-switching
+!
+interface r2-eth0
+ mpls bgp l3vpn-multi-domain-switching
+!
diff --git a/tests/topotests/bgp_vpnv4_asbr/r2/ipv4_vpn_summary.json b/tests/topotests/bgp_vpnv4_asbr/r2/ipv4_vpn_summary.json
new file mode 100644
index 0000000..d33c5f5
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/r2/ipv4_vpn_summary.json
@@ -0,0 +1,24 @@
+{
+ "routerId":"192.0.2.2",
+ "as":65500,
+ "vrfId":0,
+ "vrfName":"default",
+ "peerCount":2,
+ "peers":{
+ "192.0.2.100":{
+ "remoteAs":65500,
+ "localAs":65500,
+ "version":4,
+ "state":"Established",
+ "peerState":"OK"
+ },
+ "192.168.1.200":{
+ "remoteAs":65502,
+ "localAs":65500,
+ "version":4,
+ "state":"Established",
+ "peerState":"OK"
+ }
+ },
+ "totalPeers":2
+}
diff --git a/tests/topotests/bgp_vpnv4_asbr/r2/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/r2/zebra.conf
new file mode 100644
index 0000000..43508a4
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/r2/zebra.conf
@@ -0,0 +1,13 @@
+log stdout
+ip route 192.168.1.3/32 r2-eth1
+interface lo
+ ip address 192.0.2.2/32
+!
+interface r2-eth0
+ ip address 192.168.0.2/24
+ mpls enable
+!
+interface r2-eth1
+ ip address 192.168.1.2/24
+ mpls enable
+!
diff --git a/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf
new file mode 100644
index 0000000..c5d5727
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf
@@ -0,0 +1,25 @@
+router bgp 65501
+ bgp router-id 192.0.2.3
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.200 remote-as 65502
+ address-family ipv4 unicast
+ no neighbor 192.168.1.200 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 192.168.1.200 activate
+ exit-address-family
+!
+router bgp 65501 vrf vrf1
+ bgp router-id 192.0.2.3
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 102
+ rd vpn export 444:3
+ rt vpn both 52:100
+ export vpn
+ import vpn
+ exit-address-family
+!
+interface r3-eth0
+ mpls bgp forwarding
+!
diff --git a/tests/topotests/bgp_vpnv4_asbr/r3/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/r3/zebra.conf
new file mode 100644
index 0000000..6376785
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/r3/zebra.conf
@@ -0,0 +1,14 @@
+log stdout
+ip route 192.168.1.3/32 r3-eth0
+interface r3-eth1 vrf vrf1
+ ip address 172.31.1.1/24
+!
+interface r3-eth2 vrf vrf1
+ ip address 172.31.2.1/24
+!
+interface r3-eth3 vrf vrf1
+ ip address 172.31.3.1/24
+!
+interface r3-eth0
+ ip address 192.168.1.3/24
+!
diff --git a/tests/topotests/bgp_vpnv4_asbr/rr100/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/rr100/bgpd.conf
new file mode 100644
index 0000000..845d71b
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/rr100/bgpd.conf
@@ -0,0 +1,29 @@
+router bgp 65500
+ bgp router-id 192.0.2.100
+ no bgp ebgp-requires-policy
+ neighbor 192.0.2.2 remote-as 65500
+ neighbor 192.0.2.2 update-source lo
+ neighbor 192.168.0.2 remote-as 65500
+ neighbor 192.0.2.1 remote-as 65500
+ neighbor 192.0.2.1 update-source lo
+ neighbor 192.168.0.1 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 192.168.0.1 activate
+ no neighbor 192.0.2.1 activate
+ no neighbor 192.168.0.2 activate
+ no neighbor 192.0.2.2 activate
+ network 192.0.2.100/32
+ exit-address-family
+ address-family ipv4 labeled-unicast
+ neighbor 192.168.0.1 activate
+ neighbor 192.168.0.2 activate
+ neighbor 192.168.0.1 route-reflector-client
+ neighbor 192.168.0.2 route-reflector-client
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 192.0.2.1 activate
+ neighbor 192.0.2.2 activate
+ neighbor 192.0.2.1 route-reflector-client
+ neighbor 192.0.2.2 route-reflector-client
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_vpnv4_asbr/rr100/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/rr100/zebra.conf
new file mode 100644
index 0000000..2fa5285
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/rr100/zebra.conf
@@ -0,0 +1,7 @@
+log stdout
+interface lo
+ ip address 192.0.2.100/32
+!
+interface rr100-eth0
+ ip address 192.168.0.100/24
+!
diff --git a/tests/topotests/bgp_vpnv4_asbr/rs200/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/rs200/bgpd.conf
new file mode 100644
index 0000000..fa3cb54
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/rs200/bgpd.conf
@@ -0,0 +1,19 @@
+debug bgp nht
+debug bgp zebra
+debug bgp labelpool
+router bgp 65502
+ bgp router-id 192.0.2.200
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.3 remote-as 65501
+ neighbor 192.168.1.2 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 192.168.1.2 activate
+ no neighbor 192.168.1.3 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 192.168.1.3 activate
+ neighbor 192.168.1.2 activate
+ neighbor 192.168.1.3 route-server-client
+ neighbor 192.168.1.2 route-server-client
+ exit-address-family
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_vpnv4_asbr/rs200/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/rs200/zebra.conf
new file mode 100644
index 0000000..98793ca
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/rs200/zebra.conf
@@ -0,0 +1,4 @@
+log stdout
+interface rs200-eth0
+ ip address 192.168.1.200/24
+!
diff --git a/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py b/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py
new file mode 100644
index 0000000..a908e74
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py
@@ -0,0 +1,917 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_vpnv4_asbr.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2023 by 6WIND
+#
+
+"""
+ test_bgp_vpnv4_asbr.py: Test the FRR BGP daemon with rfc4364 option 10b
+ r1, r2, and r100 are in an iBGP AS, while r2, r3 do an eBGP peering
+ h1 is a host behind r1 VRF1, and {h2,h3} are hosts behind r3 VRF1
+ The test demonstrates the connectivity across the network between h1 and h3.
+
+
+ +----------+ +----+--------+ +--------+ +--------+-----+
+ | |172.31.0.0|vrf | r1 |192.168.0.0/24| r2 |192.168.1.0/24|r3 | vrf |
+ | h1 +----------+ | 1+------+-------+ +------+-------+3 | +--- 172.31.3.0/24
+ | 10 | |VRF1|AS65500 | | | AS65500| | |AS65501 |VRF1 |
+ +----------+ +-------------+ | +--------+ | +--------+--+-++
+ 192.0.2.1 | 192.0.2.2 | 172| |
+ +----------+ +----+--------+ 31| |
+ |rr100 | |rs200/AS65502| 1| |
+ +----------+ +-------------+ 0| |
+ 192.0.2.100 +--------+ /24| |
+ | | +----------+----+ |
+ |h3 | | | |
+ |10 | | h2 | |
+ +---+----+ | 10 | |
+ | +----------+ |
+ |172.31.2.0/24 |
+ +--------------------------------+
+"""
+
+import os
+import sys
+import json
+from functools import partial
+import pytest
+import functools
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.checkping import check_ping
+
+
+# Required to instantiate the topology builder class.
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Allocate 8 devices
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+ tgen.add_router("r3")
+ tgen.add_router("h1")
+ tgen.add_router("h2")
+ tgen.add_router("h3")
+ tgen.add_router("rr100")
+ tgen.add_router("rs200")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["rr100"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["h1"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["rs200"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["h2"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["h3"])
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r3"])
+
+
+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 rname in ("r1", "r3"):
+ for cmd in cmds_list:
+ input = cmd.format(rname)
+ logger.info("input: " + cmd)
+ output = tgen.net[rname].cmd(cmd.format(rname))
+ logger.info("output: " + output)
+
+ cmds_list = [
+ "ip link set dev {0}-eth2 master vrf1",
+ "ip link set dev {0}-eth3 master vrf1",
+ ]
+ for cmd in cmds_list:
+ input = cmd.format("r3")
+ logger.info("input: " + input)
+ output = tgen.net["r3"].cmd(input)
+ logger.info("output: " + output)
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ _populate_iface()
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ if rname in ("r1", "r2", "r3", "rr100", "rs200"):
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ tgen.stop_topology()
+
+
+def bgp_vpnv4_prefix_check(router, rd, prefix, label, nexthop):
+ """
+ Dump and check 'show bgp ipv4 vpn <prefix> json' output. An assert is triggered in case test fails
+ * 'router': the router to check
+ * 'rd': The route distinguisher expected
+ * 'prefix': The prefix expected
+ * 'label': The label expected associated with the ('rd','prefix') tuple
+ * 'nexthop': The nexthop expected associated with the ('rd','prefix') tuple
+ """
+
+ def _check(router, prefix, rd, label, nexthop):
+ dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True)
+ if not dump:
+ return "{0}, {1}, route distinguisher {2} not present".format(
+ router.name, prefix, rd
+ )
+ for dumped_rd, pathes in dump.items():
+ if dumped_rd != rd:
+ continue
+ for path in pathes["paths"]:
+ if "remoteLabel" not in path.keys():
+ return "{0}, {1}, rd {2}, remoteLabel not present".format(
+ router.name, prefix, rd
+ )
+ if str(path["remoteLabel"]) != label:
+ continue
+
+ if "nexthops" not in path.keys():
+ return "{0}, {1}, rd {2}, no nexthops present".format(
+ router.name, prefix, rd
+ )
+
+ for nh in path["nexthops"]:
+ if "ip" not in nh.keys():
+ return "{0}, {1}, rd {2}, no ipv4 nexthop available".format(
+ router.name, prefix, rd
+ )
+ if nh["ip"] != nexthop:
+ continue
+ return None
+ return "{0}, {1}, rd {2}, remoteLabel {3}, nexthop {4} not found".format(
+ router.name, prefix, rd, label, nexthop
+ )
+
+ func = functools.partial(_check, router, prefix, rd, label, nexthop)
+ success, result = topotest.run_and_expect(func, None, count=20, wait=0.5)
+ assert_msg = "{}, show bgp ipv4 vpn {}, rd {}, label {} nexthop {}".format(
+ router.name, prefix, rd, label, nexthop
+ )
+ assert result is None, assert_msg + " not found"
+ logger.info(assert_msg + " found")
+
+
+def mpls_table_get_entry(router, out_label, out_nexthop):
+ """
+ Get the in_label from tuple (out_label, out_nexthop)
+ * 'router': the router to check
+ * 'out_label': The outgoing label expected
+ * 'out_nexthop': The outgoing nexthop expected
+ """
+ dump = router.vtysh_cmd("show mpls table json", isjson=True)
+ for in_label, label_info in dump.items():
+ for nh in label_info["nexthops"]:
+ if nh["type"] != "BGP" or "installed" not in nh.keys():
+ continue
+ if "nexthop" in nh.keys():
+ if nh["nexthop"] != out_nexthop:
+ continue
+ if "outLabelStack" in nh.keys():
+ if out_label not in nh["outLabelStack"]:
+ continue
+ return in_label
+ return None
+
+
+def mpls_table_check_entry(router, out_label, out_nexthop):
+ """
+ Dump and check 'show mpls table json' output. An assert is triggered in case test fails
+ * 'router': the router to check
+ * 'out_label': The outgoing label expected
+ * 'out_nexthop': The outgoing nexthop expected
+ """
+ logger.info("Checking MPLS labels on {}".format(router.name))
+ dump = router.vtysh_cmd("show mpls table json", isjson=True)
+ for in_label, label_info in dump.items():
+ for nh in label_info["nexthops"]:
+ if nh["type"] != "BGP" or "installed" not in nh.keys():
+ continue
+ if "nexthop" in nh.keys():
+ if nh["nexthop"] != out_nexthop:
+ continue
+ if "outLabelStack" in nh.keys():
+ if out_label not in nh["outLabelStack"]:
+ continue
+ logger.info(
+ "{}, show mpls table, entry in_label {} out_label {} out_nexthop {} found".format(
+ router.name, in_label, nh["outLabelStack"], nh["nexthop"]
+ )
+ )
+ return None
+ return "{}, show mpls table, entry matching in_label {} out_label {} out_nexthop {} not found".format(
+ router.name, in_label, out_label, out_nexthop
+ )
+
+
+def check_show_bgp_vpn_prefix_found(
+ router, ipversion, prefix, rd, label=None, nexthop=None
+):
+ """
+ Check if a given vpn prefix is present in the BGP RIB
+ * 'router': the router to check BGP VPN RIB
+ * 'ipversion': The ip version to check: ipv4 or ipv6
+ * 'prefix': the IP prefix to check
+ * 'rd': the route distinguisher to check
+ * 'label: the label to check
+ """
+ output = json.loads(
+ router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix))
+ )
+ if label:
+ if nexthop:
+ expected = {
+ rd: {
+ "prefix": prefix,
+ "paths": [{"remoteLabel": label, "nexthops": [{"ip": nexthop}]}],
+ }
+ }
+ else:
+ expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}}
+ else:
+ if nexthop:
+ expected = {
+ rd: {"prefix": prefix, "paths": [{"nexthops": [{"ip": nexthop}]}]}
+ }
+ else:
+ expected = {rd: {"prefix": prefix}}
+ return topotest.json_cmp(output, expected)
+
+
+def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None):
+ """
+ Check if a given vpn prefix is not present in the BGP RIB
+ * 'router': the router to check BGP VPN RIB
+ * 'ipversion': The ip version to check: ipv4 or ipv6
+ * 'prefix': the IP prefix to check
+ * 'rd': the route distinguisher to check
+ * 'label: the label to check
+ """
+ output = json.loads(
+ router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix))
+ )
+ if label:
+ expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}}
+ else:
+ expected = {rd: {"prefix": prefix}}
+ ret = topotest.json_cmp(output, expected)
+ if ret is None:
+ return "not good"
+ return None
+
+
+def check_show_mpls_table_entry_label_not_found(router, inlabel):
+ output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel)))
+ expected = {"inLabel": inlabel, "installed": True}
+ ret = topotest.json_cmp(output, expected)
+ if ret is None:
+ return "not good"
+ return None
+
+
+def check_show_bgp_vpn_ok(router, vpnv4_entries):
+ """
+ Check on router that BGP l3vpn entries are present
+ Check there is an MPLS entry bound to that BGP L3VPN entry
+ Extract the Label value and check on the distributed router the BGP L3VPN entry
+ If check fail, an assert is triggered.
+ * 'router': the router to check BGP VPN RIB
+ * 'vpnv4_entries': dictionary that contains the list of prefixes, and the distributed router to look after
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ vpnv4_nexthops = {"r1": "192.0.2.2", "r3": "192.168.1.2"}
+ vpnv4_nht = {"192.0.2.1": "192.168.0.1", "192.168.1.3": "192.168.1.3"}
+ label_ip_entries = {}
+
+ def _return_remote_label_nh_rd(router, prefix):
+ dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True)
+ assert_msg = (
+ "{}, prefix {} not available or label not found",
+ router.name,
+ prefix,
+ )
+ assert dump, assert_msg
+ for rd, pathes in dump.items():
+ for path in pathes["paths"]:
+ if "remoteLabel" not in path.keys():
+ assert 0, assert_msg
+ for nh in path["nexthops"]:
+ if "ip" in nh.keys():
+ return path["remoteLabel"], nh["ip"], rd
+ assert 0, assert_msg
+
+ def _check_nexthop_available(router, prefix):
+ dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True)
+ if not dump:
+ return "{0}, {1}, route distinguisher not present".format(
+ router.name, prefix
+ )
+ for rd, pathes in dump.items():
+ for path in pathes["paths"]:
+ if "remoteLabel" not in path.keys():
+ return "{0}, {1}, remoteLabel not present".format(
+ router.name, prefix
+ )
+ if "nexthops" not in path.keys():
+ return "{0}, {1}, no nexthop available".format(router.name, prefix)
+ return None
+
+ for prefix, rname_to_test in vpnv4_entries.items():
+ func = functools.partial(_check_nexthop_available, router, prefix)
+ success, result = topotest.run_and_expect(func, None, count=20, wait=0.5)
+ assert result is None, "Failed to detect prefix {} on router {}".format(
+ prefix, router.name
+ )
+
+ for prefix, rname_to_test in vpnv4_entries.items():
+ l3vpn_label, l3vpn_nh, l3vpn_rd = _return_remote_label_nh_rd(router, prefix)
+ logger.info(
+ "{0}, {1}, label value is {2}, nh is {3}".format(
+ router.name, prefix, l3vpn_label, l3vpn_nh
+ )
+ )
+ test_func = functools.partial(
+ mpls_table_check_entry, router, l3vpn_label, vpnv4_nht[l3vpn_nh]
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, result
+
+ in_label = mpls_table_get_entry(router, l3vpn_label, vpnv4_nht[l3vpn_nh])
+ label_ip_entries[prefix] = in_label
+
+ bgp_vpnv4_prefix_check(
+ tgen.gears[rname_to_test],
+ l3vpn_rd,
+ prefix,
+ in_label,
+ vpnv4_nexthops[rname_to_test],
+ )
+
+ return label_ip_entries
+
+
+def test_protocols_convergence():
+ """
+ Assert that all protocols have converged
+ Check that Labels are as expected in r1, r2,and r3
+ Check ping connectivity between h1 and h2
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # check that r2 peerings are ok
+ logger.info("Checking BGP ipv4 vpn summary for r2")
+ router = tgen.gears["r2"]
+ json_file = "{}/{}/ipv4_vpn_summary.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show bgp ipv4 vpn summary json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+def test_mpls_setup_ok():
+ """
+ tests for the r1 to r3 direction: checks for prefix=('172.31.1.0/24','172.31.2.0/24','172.31.3.0/24')
+ r2. get label from 'prefix'
+ check that r2. show mpls table has an entry with outbound label set to the label from 172.31.1.0/24
+ r2. get label from mpls entry
+ check that r1: show bgp ipv4 vpn 172.31.1.0/24 has label from r2.mpls entry
+ tests for the r3 to r1 direction
+ r2. get label from 172.31.0.0/24
+ check that r2. show mpls table has an entry with outbound label set that includes the label from 172.31.0.0/24
+ r2. get label from mpls entry
+ check that r3: show bgp ipv4 vpn 172.31.0.0/24 has label from r2.mpls entry
+ check that h1. ping 172.31.1.10 (h2) is ok.
+ check that h1. ping 172.31.2.10 (h3) is ok.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears["r2"]
+
+ # diagnostic
+ logger.info("Dumping mplsvpn nexthop table")
+ router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)
+
+ vpnv4_checks = {
+ "172.31.1.0/24": "r1",
+ "172.31.2.0/24": "r1",
+ "172.31.3.0/24": "r1",
+ "172.31.0.0/24": "r3",
+ }
+ logger.info(
+ "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on all devices".format(
+ router.name
+ )
+ )
+ check_show_bgp_vpn_ok(router, vpnv4_checks)
+
+ logger.info("h1, check that ping from h1 to (h2,h3) is ok")
+ check_ping("h1", "172.31.1.10", True, 20, 0.5)
+ check_ping("h1", "172.31.2.10", True, 20, 0.5)
+
+
+def test_r3_prefixes_removed():
+ """
+ Remove BGP redistributed updates from r3.
+ Check that the BGP VPN updates from the updates are not present on r2.
+ Check that the 'show bgp ipv4 vpn' and 'show mpls table' are ok for 172.31.3.0/24
+ Remove the 172.31.3.0/24 update from BGP on r3.
+ Check that the BGP VPN updates from r3 are not present on r2.
+ Check that the 'show mpls table' entry previously seen disappeared
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears["r3"]
+ logger.info("{}, keeping only 172.31.3.0/24 network".format(router.name))
+ router.vtysh_cmd("configure terminal\ninterface r3-eth1 vrf vrf1\nshutdown\n")
+ router.vtysh_cmd("configure terminal\ninterface r3-eth2 vrf vrf1\nshutdown\n")
+
+ router = tgen.gears["r2"]
+ logger.info(
+ "{}, check that 'show bgp ipv4 vpn' has only 172.31.3.0/24 network from r3".format(
+ router.name
+ )
+ )
+
+ for prefix in ("172.31.1.0/24", "172.31.2.0/24"):
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_not_found,
+ router,
+ "ipv4",
+ prefix,
+ "444:3",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "{}, vpnv4 update {} still present".format(router.name, prefix)
+
+ # diagnostic
+ logger.info("Dumping mplsvpn nexthop table")
+ router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)
+
+ prefix = "172.31.3.0/24"
+ logger.info(
+ "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format(
+ router.name
+ )
+ )
+ vpnv4_checks = {
+ prefix: "r1",
+ }
+ label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks)
+
+ router = tgen.gears["r3"]
+ logger.info("{}, removing {} network".format(router.name, prefix))
+ router.vtysh_cmd("configure terminal\ninterface r3-eth3 vrf vrf1\nshutdown\n")
+
+ router = tgen.gears["r2"]
+ logger.info(
+ "{}, check that 'show bgp ipv4 vpn' has not {} network from r3".format(
+ router.name, prefix
+ )
+ )
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_not_found,
+ router,
+ "ipv4",
+ prefix,
+ "444:3",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "{}, vpnv4 update {} still present".format(router.name, prefix)
+
+ logger.info(
+ "{}, check that 'show mpls table {}' is not present".format(
+ router.name, label_ip_entries[prefix]
+ )
+ )
+ test_func = functools.partial(
+ check_show_mpls_table_entry_label_not_found, router, label_ip_entries[prefix]
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r1, mpls entry with in_label {} still present".format(
+ label_ip_entries[prefix]
+ )
+
+
+def test_r3_prefixes_added_back():
+ """
+ Add back the 172.31.3.0/24 network from r3
+ Check on r2 that MPLS switching entry appears when the 1st BGP update is received
+ Check the IP connectivity (h1,h2) and (h1,h3)
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears["r3"]
+ prefix = "172.31.3.0/24"
+ logger.info("{}, restoring the {} network from r3".format(router.name, prefix))
+ router.vtysh_cmd("configure terminal\ninterface r3-eth3 vrf vrf1\nno shutdown\n")
+
+ router = tgen.gears["r2"]
+ logger.info(
+ "{}, check that 'show bgp ipv4 vpn' has {} network from r3".format(
+ router.name, prefix
+ )
+ )
+
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_found,
+ router,
+ "ipv4",
+ prefix,
+ "444:3",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "{}, vpnv4 update {} not present".format(router.name, prefix)
+
+ logger.info(
+ "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format(
+ router.name
+ )
+ )
+ vpnv4_checks = {
+ prefix: "r1",
+ }
+ check_show_bgp_vpn_ok(router, vpnv4_checks)
+
+ router = tgen.gears["r3"]
+ logger.info(
+ "{}, restoring the redistribute connected prefixes from r3".format(router.name)
+ )
+ router.vtysh_cmd("configure terminal\ninterface r3-eth1 vrf vrf1\nno shutdown\n")
+ router.vtysh_cmd("configure terminal\ninterface r3-eth2 vrf vrf1\nno shutdown\n")
+ router = tgen.gears["r2"]
+ for prefix in ("172.31.1.0/24", "172.31.2.0/24"):
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_found,
+ router,
+ "ipv4",
+ prefix,
+ "444:3",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "{}, vpnv4 update {} not present".format(router.name, prefix)
+
+ # diagnostic
+ logger.info("Dumping mplsvpn nexthop table")
+ tgen.gears["r2"].vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)
+
+
+def test_unconfigure_nexthop_change_nexthop_self():
+ """
+ Get the list of labels advertised from r2 to r1
+ On r2, disable next-hop-self for 192.0.2.100 neighbor
+ Check that the list of labels are not present in 'show mpls table'
+ Check that r1 received the prefixes with the original (next-hop,label)
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+ vpnv4_checks = {
+ "172.31.1.0/24": "r1",
+ "172.31.2.0/24": "r1",
+ "172.31.3.0/24": "r1",
+ }
+ logger.info(
+ "{}, Get the list of labels allocated for prefixes from r3".format(router.name)
+ )
+ label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks)
+
+ logger.info(
+ "{}, disable next-hop-self for 192.0.2.100 neighbor".format(router.name)
+ )
+ router = tgen.gears["r2"]
+ router.vtysh_cmd(
+ "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nno neighbor 192.0.2.100 next-hop-self\n"
+ )
+
+ for prefix, label in label_ip_entries.items():
+ logger.info(
+ "{}, check mpls entry for {} with in_label {} is not present'".format(
+ router.name, prefix, label
+ )
+ )
+ test_func = functools.partial(
+ check_show_mpls_table_entry_label_not_found, router, label
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r1, mpls entry for {} with in_label {} still present".format(
+ prefix, label
+ )
+
+ router = tgen.gears["r1"]
+ for prefix, label in label_ip_entries.items():
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_not_found,
+ router,
+ "ipv4",
+ prefix,
+ "444:3",
+ label=label,
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "{}, mpls vpn update {} label {} is present".format(
+ router.name, prefix, label
+ )
+ for prefix, label in label_ip_entries.items():
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_found,
+ router,
+ "ipv4",
+ prefix,
+ "444:3",
+ nexthop="192.168.1.3",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "{}, mpls vpn update {} label {} is present".format(
+ router.name, prefix, label
+ )
+
+ # diagnostic
+ logger.info("Dumping mplsvpn nexthop table")
+ tgen.gears["r2"].vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)
+
+
+def test_reconfigure_nexthop_change_nexthop_self():
+ """
+ Get the list of labels advertised from r2 to r1
+ On r2, enable next-hop-self for 192.0.2.100 neighbor
+ Check that the list of labels are present in 'show mpls table'
+ Check that r1 received the prefixes with the original (next-hop,label)
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+ logger.info("{}, enable next-hop-self for 192.0.2.100 neighbor".format(router.name))
+ router.vtysh_cmd(
+ "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nneighbor 192.0.2.100 next-hop-self\n"
+ )
+ vpnv4_checks = {
+ "172.31.1.0/24": "r1",
+ "172.31.2.0/24": "r1",
+ "172.31.3.0/24": "r1",
+ }
+ logger.info(
+ "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format(
+ router.name
+ )
+ )
+ check_show_bgp_vpn_ok(router, vpnv4_checks)
+
+ logger.info("h1, check that ping from h1 to (h2,h3) is ok")
+ check_ping("h1", "172.31.1.10", True, 20, 0.5)
+ check_ping("h1", "172.31.2.10", True, 20, 0.5)
+ # diagnostic
+ logger.info("Dumping mplsvpn nexthop table")
+ router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)
+
+
+def test_declare_vpn_network_with_different_label():
+ """
+ declare a vpnv4 network on r3.
+ check that a new VPNv4 entry is received on r2.
+ Check that the list of labels are present in 'show mpls table'
+ Check that r1 received the prefixes with the new (next-hop,label)
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r3"]
+ logger.info(
+ "{}, declare static 33.33.33.33/32 network rd 33:33 label 33".format(
+ router.name
+ )
+ )
+ router.vtysh_cmd(
+ "configure terminal\nrouter bgp 65501\nno bgp network import-check\n"
+ )
+ router.vtysh_cmd(
+ "configure terminal\nrouter bgp 65501\naddress-family ipv4 vpn\nnetwork 33.33.33.33/32 rd 444:3 label 33\n"
+ )
+
+ router = tgen.gears["r2"]
+ vpnv4_entries = {
+ "172.31.1.0/24": None,
+ "172.31.2.0/24": None,
+ "172.31.3.0/24": None,
+ "33.33.33.33/32": 33,
+ }
+
+ for prefix, label in vpnv4_entries.items():
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_found,
+ router,
+ "ipv4",
+ prefix,
+ "444:3",
+ label=label,
+ nexthop="192.168.1.3",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "{}, vpnv4 update {}, label {} not present".format(
+ router.name, prefix, label
+ )
+
+ vpnv4_checks = {
+ "172.31.1.0/24": "r1",
+ "172.31.2.0/24": "r1",
+ "172.31.3.0/24": "r1",
+ "33.33.33.33/32": "r1",
+ }
+ logger.info(
+ "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format(
+ router.name
+ )
+ )
+ check_show_bgp_vpn_ok(router, vpnv4_checks)
+
+
+def test_filter_vpn_network_from_r1():
+ """
+ Get the list of labels in 'show mpls table'
+ filter network from r1
+ check that the vpnv4 entry on r2 is not present
+ Check that the associated mpls entry is not present
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+
+ vpnv4_checks = {
+ "172.31.0.0/24": "r3",
+ }
+ logger.info(
+ "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r3".format(
+ router.name
+ )
+ )
+ label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks)
+
+ for prefix, label in label_ip_entries.items():
+ logger.info("{}, filter prefix {} from r1".format(router.name, prefix))
+ router.vtysh_cmd(
+ "configure terminal\nroute-map rmap deny 1\nmatch ip next-hop address 192.0.2.1\n"
+ )
+ router.vtysh_cmd(
+ "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nneighbor 192.0.2.100 route-map rmap in\n"
+ )
+ logger.info(
+ "{}, check that prefix {} is not present".format(router.name, prefix)
+ )
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_not_found,
+ router,
+ "ipv4",
+ "172.31.0.0/24",
+ "444:1",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "{}, vpnv4 update {}, is still present".format(
+ router.name, prefix
+ )
+
+ # diagnostic
+ logger.info("Dumping mplsvpn nexthop table")
+ router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)
+
+ logger.info(
+ "{}, check that show mpls table {} is not present".format(
+ router.name, label
+ )
+ )
+ test_func = functools.partial(
+ check_show_mpls_table_entry_label_not_found, router, int(label)
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r1, mpls entry for {} with in_label {} still present".format(
+ prefix, label
+ )
+
+
+def test_unfilter_vpn_network_from_r1():
+ """
+ unfilter network from r1
+ check that the vpnv4 entry on r2 is present
+ Check that the list of labels are present in 'show mpls table'
+ Check that r3 received the prefixes with the new (next-hop,label)
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+ prefix = "172.31.0.0/24"
+
+ logger.info("{}, filter prefix {} from r1".format(router.name, prefix))
+ router.vtysh_cmd(
+ "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nno neighbor 192.0.2.100 route-map rmap in\n"
+ )
+
+ logger.info("{}, check that prefix {} is present".format(router.name, prefix))
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_found, router, "ipv4", prefix, "444:1"
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "{}, vpnv4 update {}, is not present".format(router.name, prefix)
+
+ vpnv4_checks = {
+ "172.31.0.0/24": "r3",
+ }
+ logger.info(
+ "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on all devices".format(
+ router.name
+ )
+ )
+ check_show_bgp_vpn_ok(router, vpnv4_checks)
+
+ # diagnostic
+ logger.info("Dumping mplsvpn nexthop table")
+ router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False)
+
+
+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_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/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_ebgp/r1/bgp_ipv4_routes.json
new file mode 100644
index 0000000..184ab31
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r1/bgp_ipv4_routes.json
@@ -0,0 +1,49 @@
+{
+ "vrfName": "vrf1",
+ "localAS": 65500,
+ "routes":
+ {
+ "172.31.0.10/32": [
+ {
+ "prefix": "172.31.0.10",
+ "prefixLen": 32,
+ "network": "172.31.0.10\/32",
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "192.168.0.3",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ },
+ {
+ "prefix": "172.31.0.10",
+ "prefixLen": 32,
+ "network": "172.31.0.10\/32",
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "192.168.0.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.31.0.1/32": [
+ {
+ "prefix": "172.31.0.1",
+ "prefixLen": 32,
+ "network": "172.31.0.1\/32",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
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..0249279
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf
@@ -0,0 +1,28 @@
+router bgp 65500
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.2 remote-as 65501
+ neighbor 192.168.0.3 remote-as 65501
+ address-family ipv4 unicast
+ no neighbor 192.168.0.3 activate
+ no neighbor 192.168.0.2 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 192.168.0.2 activate
+ neighbor 192.168.0.3 activate
+ 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
+!
+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..79b020a
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json
@@ -0,0 +1,62 @@
+{
+ "172.31.0.10/32": [
+ {
+ "prefix": "172.31.0.10/32",
+ "prefixLen": 32,
+ "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-eth0",
+ "vrf": "default",
+ "active": true,
+ "labels":[
+ 102
+ ]
+ },
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "192.168.0.3",
+ "afi": "ipv4",
+ "interfaceName": "r1-eth0",
+ "vrf": "default",
+ "active": true,
+ "labels":[
+ 102
+ ]
+ }
+ ]
+ }
+ ],
+ "172.31.0.1/32": [
+ {
+ "prefix": "172.31.0.1/32",
+ "prefixLen": 32,
+ "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..f626e44
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf
@@ -0,0 +1,7 @@
+log stdout
+interface r1-eth1 vrf vrf1
+ ip address 172.31.0.1/32
+!
+interface r1-eth0
+ ip address 192.168.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..1fc3a4b
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json
@@ -0,0 +1,38 @@
+{
+ "vrfName": "vrf1",
+ "localAS": 65501,
+ "routes":
+ {
+ "172.31.0.1/32": [
+ {
+ "prefix": "172.31.0.1",
+ "prefixLen": 32,
+ "network": "172.31.0.1\/32",
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.31.0.10/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172.31.0.10",
+ "prefixLen": 32,
+ "network": "172.31.0.10\/32",
+ "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..e873469
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf
@@ -0,0 +1,25 @@
+router bgp 65501
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.1 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 192.168.0.1 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 192.168.0.1 activate
+ exit-address-family
+!
+router bgp 65501 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
+!
+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..bbc5240
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf
@@ -0,0 +1,7 @@
+log stdout
+interface r2-eth1 vrf vrf1
+ ip address 172.31.0.10/32
+!
+interface r2-eth0
+ ip address 192.168.0.2/24
+!
diff --git a/tests/topotests/bgp_vpnv4_ebgp/r3/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_ebgp/r3/bgp_ipv4_routes.json
new file mode 100644
index 0000000..19797dd
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r3/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/r3/bgpd.conf b/tests/topotests/bgp_vpnv4_ebgp/r3/bgpd.conf
new file mode 100644
index 0000000..a327638
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r3/bgpd.conf
@@ -0,0 +1,25 @@
+router bgp 65501
+ bgp router-id 192.0.2.3
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.1 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 192.168.0.1 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 192.168.0.1 activate
+ exit-address-family
+!
+router bgp 65502 vrf vrf1
+ bgp router-id 192.0.2.3
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 102
+ rd vpn export 444:3
+ rt vpn both 52:100
+ export vpn
+ import vpn
+ exit-address-family
+!
+interface r3-eth0
+ mpls bgp forwarding
+!
diff --git a/tests/topotests/bgp_vpnv4_ebgp/r3/zebra.conf b/tests/topotests/bgp_vpnv4_ebgp/r3/zebra.conf
new file mode 100644
index 0000000..4412c04
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r3/zebra.conf
@@ -0,0 +1,7 @@
+log stdout
+interface r3-eth1 vrf vrf1
+ ip address 172.31.0.10/32
+!
+interface r3-eth0
+ ip address 192.168.0.3/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..61e1163
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py
@@ -0,0 +1,228 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_vpnv4_ebgp.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2022 by 6WIND
+#
+
+"""
+ 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")
+ tgen.add_router("r3")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+ 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["r3"])
+
+
+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")
+ logger.info("input: " + cmd)
+ output = tgen.net["r1"].cmd(cmd.format("r1"))
+ logger.info("output: " + output)
+
+ for cmd in cmds_list:
+ input = cmd.format("r2")
+ logger.info("input: " + cmd)
+ output = tgen.net["r2"].cmd(cmd.format("r2"))
+ logger.info("output: " + output)
+
+ for cmd in cmds_list:
+ input = cmd.format("r3")
+ logger.info("input: " + cmd)
+ output = tgen.net["r3"].cmd(cmd.format("r3"))
+ 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 r1
+ logger.info("Checking BGP IPv4 routes for convergence on r1")
+ router = tgen.gears["r1"]
+ 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
+
+ # Check BGP IPv4 imported entry is not detected as local
+ # "selectionReason": "Locally configured route"
+ donna = tgen.gears["r1"].vtysh_cmd(
+ "show bgp vrf vrf1 ipv4 172.31.0.10/32 json", isjson=True
+ )
+ routes = donna["paths"]
+ selectionReasonFound = False
+ for route in routes:
+ if "bestpath" not in route.keys():
+ continue
+ if "selectionReason" not in route["bestpath"].keys():
+ continue
+
+ if "Locally configured route" == route["bestpath"]["selectionReason"]:
+ assert 0, "imported prefix has wrong reason detected"
+
+ selectionReasonFound = True
+
+ if not selectionReasonFound:
+ assertmsg = '"{}" imported prefix has wrong reason detected'.format(router.name)
+ assert False, 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..295811b
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf
@@ -0,0 +1,27 @@
+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
+ distance bgp 21 201 41
+ 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..e57e21b
--- /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": 201,
+ "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..6f313be
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_vpnv4_gre.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by 6WIND
+#
+
+"""
+ 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..0709e43
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf
@@ -0,0 +1,46 @@
+router bgp 65500
+ bgp router-id 192.0.2.1
+ neighbor 10.125.0.2 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 10.125.0.2 activate
+ label vpn export 100
+ rd vpn export 192.0.2.1:0
+ rt vpn import 192.0.2.2:400
+ import vpn
+ 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 192.0.2.1
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 101
+ rd vpn export 192.0.2.1:1
+ rt vpn import 192.0.2.2:100
+ rt vpn export 192.0.2.1:100
+ export vpn
+ import vpn
+ exit-address-family
+!
+router bgp 65500 vrf vrf3
+ bgp router-id 192.0.2.1
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 103
+ rd vpn export 192.0.2.1:3
+ rt vpn export 192.0.2.1:300
+ export vpn
+ exit-address-family
+!
+router bgp 65500 vrf vrf4
+ bgp router-id 192.0.2.1
+ address-family ipv4 unicast
+ label vpn export 104
+ rd vpn export 192.0.2.1:4
+ rt vpn import 192.0.2.1:300
+ import vpn
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_all.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_all.json
new file mode 100644
index 0000000..648bf85
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_all.json
@@ -0,0 +1,175 @@
+{
+ "vrfId":0,
+ "vrfName":"default",
+ "routerId":"192.0.2.1",
+ "defaultLocPrf":100,
+ "localAS":65500,
+ "routes":{
+ "routeDistinguishers":{
+ "192.0.2.1:1":{
+ "10.101.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"10.101.0.0",
+ "prefixLen":24,
+ "network":"10.101.0.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"incomplete",
+ "announceNexthopSelf":true,
+ "nhVrfName":"vrf1",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.1:3":{
+ "10.103.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"10.103.0.0",
+ "prefixLen":24,
+ "network":"10.103.0.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"incomplete",
+ "announceNexthopSelf":true,
+ "nhVrfName":"vrf3",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:1":{
+ "10.201.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.201.0.0",
+ "prefixLen":24,
+ "network":"10.201.0.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:2":{
+ "10.202.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.202.0.0",
+ "prefixLen":24,
+ "network":"10.202.0.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:3":{
+ "10.203.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.203.0.0",
+ "prefixLen":24,
+ "network":"10.203.0.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:4":{
+ "10.204.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.204.0.0",
+ "prefixLen":24,
+ "network":"10.204.0.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init.json
new file mode 100644
index 0000000..f01607a
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init.json
@@ -0,0 +1,121 @@
+{
+ "vrfId":0,
+ "vrfName":"default",
+ "routerId":"192.0.2.1",
+ "defaultLocPrf":100,
+ "localAS":65500,
+ "routes":{
+ "routeDistinguishers":{
+ "192.0.2.1:1":{
+ "10.101.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"10.101.0.0",
+ "prefixLen":24,
+ "network":"10.101.0.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"incomplete",
+ "announceNexthopSelf":true,
+ "nhVrfName":"vrf1",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.1:3":{
+ "10.103.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"10.103.0.0",
+ "prefixLen":24,
+ "network":"10.103.0.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"incomplete",
+ "announceNexthopSelf":true,
+ "nhVrfName":"vrf3",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:1":{
+ "10.201.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.201.0.0",
+ "prefixLen":24,
+ "network":"10.201.0.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:4":{
+ "10.204.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.204.0.0",
+ "prefixLen":24,
+ "network":"10.204.0.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json
new file mode 100644
index 0000000..6df6c69
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json
@@ -0,0 +1,148 @@
+{
+ "vrfId":0,
+ "vrfName":"default",
+ "routerId":"192.0.2.1",
+ "defaultLocPrf":100,
+ "localAS":65500,
+ "routes":{
+ "routeDistinguishers":{
+ "192.0.2.1:1":{
+ "10.101.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"10.101.0.0",
+ "prefixLen":24,
+ "network":"10.101.0.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"incomplete",
+ "announceNexthopSelf":true,
+ "nhVrfName":"vrf1",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.1:3":{
+ "10.103.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"10.103.0.0",
+ "prefixLen":24,
+ "network":"10.103.0.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"incomplete",
+ "announceNexthopSelf":true,
+ "nhVrfName":"vrf3",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:1":{
+ "10.201.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.201.0.0",
+ "prefixLen":24,
+ "network":"10.201.0.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:2":{
+ "10.202.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.202.0.0",
+ "prefixLen":24,
+ "network":"10.202.0.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:4":{
+ "10.204.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.204.0.0",
+ "prefixLen":24,
+ "network":"10.204.0.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json
new file mode 100644
index 0000000..7a17ff0
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json
@@ -0,0 +1,148 @@
+{
+ "vrfId":0,
+ "vrfName":"default",
+ "routerId":"192.0.2.1",
+ "defaultLocPrf":100,
+ "localAS":65500,
+ "routes":{
+ "routeDistinguishers":{
+ "192.0.2.1:1":{
+ "10.101.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"10.101.0.0",
+ "prefixLen":24,
+ "network":"10.101.0.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"incomplete",
+ "announceNexthopSelf":true,
+ "nhVrfName":"vrf1",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.1:3":{
+ "10.103.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"10.103.0.0",
+ "prefixLen":24,
+ "network":"10.103.0.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"incomplete",
+ "announceNexthopSelf":true,
+ "nhVrfName":"vrf3",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:1":{
+ "10.201.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.201.0.0",
+ "prefixLen":24,
+ "network":"10.201.0.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:3":{
+ "10.203.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.203.0.0",
+ "prefixLen":24,
+ "network":"10.203.0.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:4":{
+ "10.204.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.204.0.0",
+ "prefixLen":24,
+ "network":"10.204.0.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_init.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_init.json
new file mode 100644
index 0000000..2769c6e
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_init.json
@@ -0,0 +1,156 @@
+{
+ "default": {
+ "vrfName": "default",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.204.0.0/24": [
+ {
+ "pathFrom": "external",
+ "prefix": "10.204.0.0",
+ "prefixLen": 24,
+ "network": "10.204.0.0/24",
+ "metric": 0,
+ "locPrf": 100,
+ "weight": 0,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "10.125.0.2",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf1": {
+ "vrfName": "vrf1",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.101.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.101.0.0",
+ "prefixLen": 24,
+ "network": "10.101.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.201.0.0/24": [
+ {
+ "pathFrom": "external",
+ "prefix": "10.201.0.0",
+ "prefixLen": 24,
+ "network": "10.201.0.0/24",
+ "metric": 0,
+ "locPrf": 100,
+ "weight": 0,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "10.125.0.2",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf3": {
+ "vrfName": "vrf3",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.103.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.103.0.0",
+ "prefixLen": 24,
+ "network": "10.103.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf4": {
+ "vrfName": "vrf4",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.103.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.103.0.0",
+ "prefixLen": 24,
+ "network": "10.103.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf3",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r1_vrf1.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r1_vrf1.json
new file mode 100644
index 0000000..488dc4a
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r1_vrf1.json
@@ -0,0 +1,190 @@
+{
+ "default": {
+ "vrfName": "default",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.204.0.0/24": [
+ {
+ "pathFrom": "external",
+ "prefix": "10.204.0.0",
+ "prefixLen": 24,
+ "network": "10.204.0.0/24",
+ "metric": 0,
+ "locPrf": 100,
+ "weight": 0,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "10.125.0.2",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf1": {
+ "vrfName": "vrf1",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.101.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.101.0.0",
+ "prefixLen": 24,
+ "network": "10.101.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.201.0.0/24": [
+ {
+ "pathFrom": "external",
+ "prefix": "10.201.0.0",
+ "prefixLen": 24,
+ "network": "10.201.0.0/24",
+ "metric": 0,
+ "locPrf": 100,
+ "weight": 0,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "10.125.0.2",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf3": {
+ "vrfName": "vrf3",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.103.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.103.0.0",
+ "prefixLen": 24,
+ "network": "10.103.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf4": {
+ "vrfName": "vrf4",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.103.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.103.0.0",
+ "prefixLen": 24,
+ "network": "10.103.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf3",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf2": {
+ "vrfName": "vrf2",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.101.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.101.0.0",
+ "prefixLen": 24,
+ "network": "10.101.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf1",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf2.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf2.json
new file mode 100644
index 0000000..b751756
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf2.json
@@ -0,0 +1,188 @@
+{
+ "default": {
+ "vrfName": "default",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.204.0.0/24": [
+ {
+ "pathFrom": "external",
+ "prefix": "10.204.0.0",
+ "prefixLen": 24,
+ "network": "10.204.0.0/24",
+ "metric": 0,
+ "locPrf": 100,
+ "weight": 0,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "10.125.0.2",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf1": {
+ "vrfName": "vrf1",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.101.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.101.0.0",
+ "prefixLen": 24,
+ "network": "10.101.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.201.0.0/24": [
+ {
+ "pathFrom": "external",
+ "prefix": "10.201.0.0",
+ "prefixLen": 24,
+ "network": "10.201.0.0/24",
+ "metric": 0,
+ "locPrf": 100,
+ "weight": 0,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "10.125.0.2",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf3": {
+ "vrfName": "vrf3",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.103.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.103.0.0",
+ "prefixLen": 24,
+ "network": "10.103.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf4": {
+ "vrfName": "vrf4",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.103.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.103.0.0",
+ "prefixLen": 24,
+ "network": "10.103.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf3",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf2": {
+ "vrfName": "vrf2",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.202.0.0/24": [
+ {
+ "pathFrom": "external",
+ "prefix": "10.202.0.0",
+ "prefixLen": 24,
+ "network": "10.202.0.0/24",
+ "metric": 0,
+ "locPrf": 100,
+ "weight": 0,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "10.125.0.2",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf3.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf3.json
new file mode 100644
index 0000000..49d4066
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vrf_all_routes_plus_r2_vrf3.json
@@ -0,0 +1,188 @@
+{
+ "default": {
+ "vrfName": "default",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.204.0.0/24": [
+ {
+ "pathFrom": "external",
+ "prefix": "10.204.0.0",
+ "prefixLen": 24,
+ "network": "10.204.0.0/24",
+ "metric": 0,
+ "locPrf": 100,
+ "weight": 0,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "10.125.0.2",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf1": {
+ "vrfName": "vrf1",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.101.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.101.0.0",
+ "prefixLen": 24,
+ "network": "10.101.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.201.0.0/24": [
+ {
+ "pathFrom": "external",
+ "prefix": "10.201.0.0",
+ "prefixLen": 24,
+ "network": "10.201.0.0/24",
+ "metric": 0,
+ "locPrf": 100,
+ "weight": 0,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "10.125.0.2",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf3": {
+ "vrfName": "vrf3",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.103.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.103.0.0",
+ "prefixLen": 24,
+ "network": "10.103.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf4": {
+ "vrfName": "vrf4",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.103.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.103.0.0",
+ "prefixLen": 24,
+ "network": "10.103.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf3",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "vrf2": {
+ "vrfName": "vrf2",
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "10.203.0.0/24": [
+ {
+ "pathFrom": "external",
+ "prefix": "10.203.0.0",
+ "prefixLen": 24,
+ "network": "10.203.0.0/24",
+ "metric": 0,
+ "locPrf": 100,
+ "weight": 0,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "10.125.0.2",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
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..f99cfaf
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf
@@ -0,0 +1,16 @@
+log stdout
+interface lo
+ ip address 192.0.2.1/32
+!
+interface r1-gre0
+ ip address 192.168.0.1/24
+!
+interface r1-eth0
+ ip address 10.125.0.1/24
+!
+interface r1-eth1 vrf vrf1
+ ip address 10.101.0.1/24
+!
+interface r1-eth3 vrf vrf3
+ ip address 10.103.0.1/24
+! \ No newline at end of file
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..729daef
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf
@@ -0,0 +1,54 @@
+router bgp 65500
+ bgp router-id 192.0.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
+ exit-address-family
+!
+router bgp 65500 vrf vrf1
+ bgp router-id 192.0.2.2
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 201
+ rd vpn export 192.0.2.2:1
+ rt vpn import 192.0.2.1:100 192.0.2.2:100 192.0.2.2:200
+ rt vpn export 192.0.2.2:100
+ export vpn
+ import vpn
+ exit-address-family
+!
+router bgp 65500 vrf vrf2
+ bgp router-id 192.0.2.2
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 202
+ rd vpn export 192.0.2.2:2
+ rt vpn import 192.0.2.1:100 192.0.2.2:100 192.0.2.2:200
+ rt vpn export 192.0.2.2:200
+ export vpn
+ import vpn
+ exit-address-family
+!
+router bgp 65500 vrf vrf3
+ bgp router-id 192.0.2.2
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 203
+ rd vpn export 192.0.2.2:3
+ rt vpn export 192.0.2.2:300
+ export vpn
+ exit-address-family
+!
+router bgp 65500 vrf vrf4
+ bgp router-id 192.0.2.2
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 204
+ rd vpn export 192.0.2.2:4
+ rt vpn export 192.0.2.2:400
+ export vpn
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_routes_all.json b/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_routes_all.json
new file mode 100644
index 0000000..d8b8e88
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_routes_all.json
@@ -0,0 +1,177 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "routerId":"192.0.2.2",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "routeDistinguishers": {
+ "192.0.2.1:1": {
+ "10.101.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "internal",
+ "prefix": "10.101.0.0",
+ "prefixLen": 24,
+ "network": "10.101.0.0/24",
+ "metric": 0,
+ "locPrf": 100,
+ "weight": 0,
+ "peerId": "10.125.0.1",
+ "path": "",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "10.125.0.1",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.1:3": {
+ "10.103.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "internal",
+ "prefix": "10.103.0.0",
+ "prefixLen": 24,
+ "network": "10.103.0.0/24",
+ "metric": 0,
+ "locPrf": 100,
+ "weight": 0,
+ "peerId": "10.125.0.1",
+ "path": "",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "10.125.0.1",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:1": {
+ "10.201.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "network": "10.201.0.0/24",
+ "prefixLen": 24,
+ "prefix": "10.201.0.0",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf1",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:2": {
+ "10.202.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "network": "10.202.0.0/24",
+ "prefixLen": 24,
+ "prefix": "10.202.0.0",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf2",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:3": {
+ "10.203.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.203.0.0",
+ "prefixLen": 24,
+ "network": "10.203.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf3",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "192.0.2.2:4": {
+ "10.204.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "10.204.0.0",
+ "prefixLen": 24,
+ "network": "10.204.0.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf4",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_summary.json b/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_summary.json
new file mode 100644
index 0000000..a4408f1
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_summary.json
@@ -0,0 +1,17 @@
+{
+ "routerId":"192.0.2.2",
+ "as":65500,
+ "vrfId":0,
+ "vrfName":"default",
+ "peerCount":1,
+ "peers":{
+ "10.125.0.1":{
+ "remoteAs":65500,
+ "localAs":65500,
+ "version":4,
+ "state":"Established",
+ "peerState":"OK"
+ }
+ },
+ "totalPeers":1
+}
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..f19ad9d
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf
@@ -0,0 +1,22 @@
+log stdout
+interface lo
+ ip address 192.0.2.2/32
+!
+interface r2-gre0
+ ip address 192.168.0.2/24
+!
+interface r2-eth0
+ ip address 10.125.0.2/24
+!
+interface r2-eth1 vrf vrf1
+ ip address 10.201.0.2/24
+!
+interface r2-eth2 vrf vrf2
+ ip address 10.202.0.2/24
+!
+interface r2-eth3 vrf vrf3
+ ip address 10.203.0.1/24
+!
+interface r2-eth4 vrf vrf4
+ ip address 10.204.0.1/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..037dd40
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py
@@ -0,0 +1,567 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_vpnv4_noretain.py
+# Part of NetDEF Topology Tests
+#
+# Copyright 2022 6WIND S.A.
+#
+
+"""
+ 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
+from copy import deepcopy
+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"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r1"])
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["r1"])
+
+ switch = tgen.add_switch("s8")
+ 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",
+ "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",
+ "ip link add vrf3 type vrf table 30",
+ "ip link set dev vrf3 up",
+ "ip link set dev {0}-eth3 master vrf3",
+ "echo 1 > /proc/sys/net/mpls/conf/vrf3/input",
+ "ip link add vrf4 type vrf table 40",
+ "ip link set dev vrf4 up",
+ "ip link set dev {0}-eth4 master vrf4",
+ "echo 1 > /proc/sys/net/mpls/conf/vrf4/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 router_json_cmp_exact_filter(router, cmd, expected):
+ output = router.vtysh_cmd(cmd)
+ logger.info("{}: {}\n{}".format(router.name, cmd, output))
+
+ json_output = json.loads(output)
+
+ # filter out tableVersion, version and nhVrfID
+ json_output.pop("tableVersion")
+ for rd, data in json_output["routes"]["routeDistinguishers"].items():
+ for prefix, attrs in data.items():
+ for attr in attrs:
+ if "nhVrfId" in attr:
+ attr.pop("nhVrfId")
+ if "version" in attr:
+ attr.pop("version")
+
+ # filter out RD with no data (e.g. "444:3": {})
+ json_tmp = deepcopy(json_output)
+ for rd, data in json_tmp["routes"]["routeDistinguishers"].items():
+ if len(data.keys()) == 0:
+ json_output["routes"]["routeDistinguishers"].pop(rd)
+
+ return topotest.json_cmp(json_output, expected, exact=True)
+
+
+def router_vrf_json_cmp_exact_filter(router, cmd, expected):
+ output = router.vtysh_cmd(cmd)
+ logger.info("{}: {}\n{}".format(router.name, cmd, output))
+
+ json_output = json.loads(output)
+
+ # filter out tableVersion, version, nhVrfId and vrfId
+ for vrf, data in json_output.items():
+ if "vrfId" in data:
+ data.pop("vrfId")
+ if "tableVersion" in data:
+ data.pop("tableVersion")
+ if "routes" not in data:
+ continue
+ for route, attrs in data["routes"].items():
+ for attr in attrs:
+ if "nhVrfId" in attr:
+ attr.pop("nhVrfId")
+ if "version" in attr:
+ attr.pop("version")
+
+ # filter out VRF with no routes
+ json_tmp = deepcopy(json_output)
+ for vrf, data in json_tmp.items():
+ if "routes" not in data or len(data["routes"].keys()) == 0:
+ json_output.pop(vrf)
+
+ return topotest.json_cmp(json_output, expected, exact=True)
+
+
+def check_show_bgp_ipv4_vpn(rname, json_file):
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears[rname]
+
+ logger.info("Checking VPNv4 routes for convergence on {}".format(rname))
+
+ json_file = "{}/{}/{}".format(CWD, router.name, json_file)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ router_json_cmp_exact_filter,
+ 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 check_show_bgp_vrf_ipv4(rname, json_file):
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears[rname]
+
+ logger.info("Checking VRF IPv4 routes for convergence on {}".format(rname))
+
+ json_file = "{}/{}/{}".format(CWD, router.name, json_file)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ router_vrf_json_cmp_exact_filter,
+ router,
+ "show bgp vrf all ipv4 unicast 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_protocols_convergence_step0():
+ """
+ Assert that all protocols have converged
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # check that r2 peerings are ok
+ logger.info("Checking BGP ipv4 vpn summary for r2")
+ router = tgen.gears["r2"]
+ json_file = "{}/{}/ipv4_vpn_summary.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show bgp ipv4 vpn summary json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+def test_bgp_no_retain_step1():
+ """
+ Check bgp no retain route-target all on r1
+ """
+
+ rname = "r1"
+ check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json")
+ check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json")
+
+ check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json")
+
+
+def test_bgp_retain_step2():
+ """
+ Apply and check bgp retain route-target all on r1
+ """
+ rname = "r1"
+ cfg = """
+configure
+router bgp 65500
+ address-family ipv4 vpn
+ bgp retain route-target all
+"""
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears[rname]
+ router.vtysh_cmd(cfg)
+
+ check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_all.json")
+ check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json")
+
+ check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json")
+
+
+def test_bgp_no_retain_step3():
+ """
+ Apply and check no bgp retain route-target all on r1
+ """
+ rname = "r1"
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+ router.vtysh_cmd(
+ "configure\nrouter bgp 65500\naddress-family ipv4 vpn\nno bgp retain route-target all\n"
+ )
+
+ check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json")
+ check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json")
+
+ check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json")
+
+
+def test_bgp_no_retain_add_vrf2_step4():
+ """
+ Add vrf2 on r1 and check bgp tables
+ """
+
+ rname = "r1"
+ cfg = """
+configure
+router bgp 65500 vrf vrf2
+ bgp router-id 192.0.2.1
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 102
+ rd vpn export 192.0.2.1:200
+ rt vpn import 192.0.2.2:200
+ import vpn
+ exit-address-family
+!
+"""
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears[rname]
+ router.vtysh_cmd(cfg)
+
+ check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json")
+ check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json")
+
+ check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_plus_r2_vrf2.json")
+
+
+def test_bgp_no_retain_unimport_vrf2_step5():
+ """
+ Unimport to vrf2 on r1 and check bgp tables
+ """
+
+ rname = "r1"
+ cfg = """
+configure
+router bgp 65500 vrf vrf2
+ address-family ipv4 unicast
+ no import vpn
+ exit-address-family
+!
+"""
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears[rname]
+ router.vtysh_cmd(cfg)
+
+ check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json")
+ check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json")
+
+ check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json")
+
+
+def test_bgp_no_retain_import_vrf2_step6():
+ """
+ Re-import to vrf2 on r1 and check bgp tables
+ """
+
+ rname = "r1"
+ cfg = """
+configure
+router bgp 65500 vrf vrf2
+ address-family ipv4 unicast
+ import vpn
+ exit-address-family
+!
+"""
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears[rname]
+ router.vtysh_cmd(cfg)
+
+ check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json")
+ check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json")
+
+ check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_plus_r2_vrf2.json")
+
+
+def test_bgp_no_retain_import_vrf1_step7():
+ """
+ Import r1 vrf1 into r1 vrf2 and check bgp tables
+ """
+
+ rname = "r1"
+ cfg = """
+configure
+router bgp 65500 vrf vrf2
+ address-family ipv4 unicast
+ rt vpn import 192.0.2.1:100
+ exit-address-family
+!
+"""
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears[rname]
+ router.vtysh_cmd(cfg)
+
+ check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json")
+ check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json")
+
+ check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_plus_r1_vrf1.json")
+
+
+def test_bgp_no_retain_import_vrf3_step8():
+ """
+ Import r2 vrf3 into r1 vrf2 and check bgp tables
+ """
+
+ rname = "r1"
+ cfg = """
+configure
+router bgp 65500 vrf vrf2
+ address-family ipv4 unicast
+ rt vpn import 192.0.2.2:300
+ exit-address-family
+!
+"""
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears[rname]
+ router.vtysh_cmd(cfg)
+
+ check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json")
+ check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json")
+
+ check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_plus_r2_vrf3.json")
+
+
+def test_bgp_no_retain_unimport_vrf3_step9():
+ """
+ Un-import r2 vrf3 into r1 vrf2 and check bgp tables
+ """
+
+ rname = "r1"
+ cfg = """
+configure
+router bgp 65500 vrf vrf2
+ address-family ipv4 unicast
+ no rt vpn import 192.0.2.2:300
+ exit-address-family
+!
+"""
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears[rname]
+ router.vtysh_cmd(cfg)
+
+ check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json")
+ check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json")
+
+ check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json")
+
+
+def test_bgp_no_retain_import_vrf3_step10():
+ """
+ Import r2 vrf3 into r1 vrf2 and check bgp tables
+ """
+
+ rname = "r1"
+ cfg = """
+configure
+router bgp 65500 vrf vrf2
+ address-family ipv4 unicast
+ rt vpn import 192.0.2.2:300
+ exit-address-family
+!
+"""
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears[rname]
+ router.vtysh_cmd(cfg)
+
+ check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json")
+ check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json")
+
+ check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_plus_r2_vrf3.json")
+
+
+def test_bgp_no_retain_remove_vrf2_step11():
+ """
+ Remove BGP vrf2 on r1 and check bgp tables
+ """
+
+ rname = "r1"
+ cfg = """
+configure
+no router bgp 65500 vrf vrf2
+"""
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears[rname]
+ router.vtysh_cmd(cfg)
+
+ check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json")
+ check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json")
+
+ check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json")
+
+
+def test_bgp_retain_step12():
+ """
+ Configure retain and check bgp tables
+ """
+
+ rname = "r1"
+ cfg = """
+configure
+router bgp 65500
+ address-family ipv4 vpn
+ bgp retain route-target all
+"""
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears[rname]
+ router.vtysh_cmd(cfg)
+
+ check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_all.json")
+ check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json")
+
+ check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.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/bgp_vpnv4_per_nexthop_label/__init__.py b/tests/topotests/bgp_vpnv4_per_nexthop_label/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/__init__.py
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgp_ipv4_routes_vrf1.json b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgp_ipv4_routes_vrf1.json
new file mode 100644
index 0000000..31a1f3d
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgp_ipv4_routes_vrf1.json
@@ -0,0 +1,143 @@
+{
+ "vrfName": "vrf1",
+ "localAS": 65500,
+ "routes":
+ {
+ "10.200.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "10.200.0.0",
+ "prefixLen": 24,
+ "network": "10.200.0.0\/24",
+ "nexthops": [
+ {
+ "ip": "192.168.0.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+
+ ],
+ "172.31.0.11/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172.31.0.11",
+ "prefixLen":32,
+ "network":"172.31.0.11/32",
+ "peerId":"192.0.2.100",
+ "nexthops":[
+ {
+ "ip":"192.0.2.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ],
+ "172.31.0.12/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172.31.0.12",
+ "prefixLen":32,
+ "network":"172.31.0.12/32",
+ "peerId":"192.0.2.100",
+ "nexthops":[
+ {
+ "ip":"192.0.2.12",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ],
+ "172.31.0.13/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172.31.0.13",
+ "prefixLen":32,
+ "network":"172.31.0.13/32",
+ "peerId":"192.168.255.13",
+ "nexthops":[
+ {
+ "ip":"192.168.255.13",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ],
+ "172.31.0.14/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172.31.0.14",
+ "prefixLen":32,
+ "network":"172.31.0.14/32",
+ "peerId":"(unspec)",
+ "nexthops":[
+ {
+ "ip":"192.0.2.14",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ],
+ "172.31.0.15/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172.31.0.15",
+ "prefixLen":32,
+ "network":"172.31.0.15/32",
+ "peerId":"(unspec)",
+ "nexthops":[
+ {
+ "ip":"192.0.2.12",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ],
+ "172.31.0.20/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172.31.0.20",
+ "prefixLen":32,
+ "network":"172.31.0.20/32",
+ "peerId":"192.0.2.100",
+ "nexthops":[
+ {
+ "ip":"192.0.2.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ],
+ "172.31.0.111/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172.31.0.111",
+ "prefixLen":32,
+ "network":"172.31.0.111/32",
+ "peerId":"192.0.2.100",
+ "nexthops":[
+ {
+ "ip":"192.0.2.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgpd.conf
new file mode 100644
index 0000000..35fb2ec
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgpd.conf
@@ -0,0 +1,30 @@
+router bgp 65500
+ bgp router-id 192.168.0.1
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.2 remote-as 65501
+ address-family ipv4 unicast
+ no neighbor 192.168.0.2 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 192.168.0.2 activate
+ neighbor 192.168.0.2 soft-reconfiguration inbound
+ exit-address-family
+!
+router bgp 65500 vrf vrf1
+ bgp router-id 192.168.0.1
+ neighbor 192.0.2.100 remote-as 65500
+ neighbor 192.168.255.13 remote-as 65500
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute static
+ label vpn export allocation-mode per-nexthop
+ label vpn export auto
+ rd vpn export 444:1
+ rt vpn both 52:100
+ export vpn
+ import vpn
+ exit-address-family
+!
+interface r1-eth0
+ mpls bgp forwarding
+!
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/ipv4_routes.json b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/ipv4_routes.json
new file mode 100644
index 0000000..da7d281
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/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_per_nexthop_label/r1/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/zebra.conf
new file mode 100644
index 0000000..2618595
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/zebra.conf
@@ -0,0 +1,18 @@
+log stdout
+debug zebra nht
+!debug zebra kernel msgdump recv
+!debug zebra dplane detailed
+!debug zebra packet recv
+interface r1-eth1 vrf vrf1
+ ip address 192.0.2.1/24
+!
+interface r1-eth2 vrf vrf1
+ ip address 192.168.255.1/24
+!
+interface r1-eth0
+ ip address 192.168.0.1/24
+!
+vrf vrf1
+ ip route 172.31.0.14/32 192.0.2.14
+ ip route 172.31.0.15/32 192.0.2.12
+exit-vrf
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/bgpd.conf
new file mode 100644
index 0000000..5da9151
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/bgpd.conf
@@ -0,0 +1,11 @@
+router bgp 65500
+ bgp router-id 192.0.2.11
+ no bgp network import-check
+ neighbor 192.0.2.100 remote-as 65500
+ address-family ipv4 unicast
+ network 172.31.0.11/32
+ network 172.31.0.111/32
+ network 172.31.0.20/32
+ exit-address-family
+!
+
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/zebra.conf
new file mode 100644
index 0000000..a080757
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/zebra.conf
@@ -0,0 +1,4 @@
+log stdout
+interface r11-eth0
+ ip address 192.0.2.11/24
+!
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/bgpd.conf
new file mode 100644
index 0000000..d3889f5
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65500
+ bgp router-id 192.0.2.12
+ no bgp network import-check
+ neighbor 192.0.2.100 remote-as 65500
+ address-family ipv4 unicast
+ network 172.31.0.12/32
+ exit-address-family
+!
+
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/zebra.conf
new file mode 100644
index 0000000..9ce3aba
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/zebra.conf
@@ -0,0 +1,4 @@
+log stdout
+interface r12-eth0
+ ip address 192.0.2.12/24
+!
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/bgpd.conf
new file mode 100644
index 0000000..21dbb58
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65500
+ bgp router-id 192.168.255.13
+ no bgp network import-check
+ address-family ipv4 unicast
+ neighbor 192.168.255.1 remote-as 65500
+ network 172.31.0.13/32
+ exit-address-family
+!
+
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/zebra.conf
new file mode 100644
index 0000000..4d78b5f
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/zebra.conf
@@ -0,0 +1,4 @@
+log stdout
+interface r13-eth0
+ ip address 192.168.255.13/24
+!
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_ipv4_routes.json
new file mode 100644
index 0000000..3407925
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/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": "192.168.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_per_nexthop_label/r2/bgp_vpnv4_routes.json b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_vpnv4_routes.json
new file mode 100644
index 0000000..46f4a18
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_vpnv4_routes.json
@@ -0,0 +1,187 @@
+{
+ "vrfName": "default",
+ "localAS": 65501,
+ "routes":
+ {
+ "routeDistinguishers":
+ {
+ "444:1":
+ {
+ "172.31.0.11/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172.31.0.11",
+ "prefixLen": 32,
+ "network": "172.31.0.11\/32",
+ "peerId": "192.168.0.1",
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.31.0.12/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172.31.0.12",
+ "prefixLen": 32,
+ "network": "172.31.0.12\/32",
+ "peerId": "192.168.0.1",
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.31.0.13/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172.31.0.13",
+ "prefixLen": 32,
+ "network": "172.31.0.13\/32",
+ "peerId": "192.168.0.1",
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.31.0.14/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172.31.0.14",
+ "prefixLen": 32,
+ "network": "172.31.0.14\/32",
+ "peerId": "192.168.0.1",
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.31.0.15/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172.31.0.15",
+ "prefixLen": 32,
+ "network": "172.31.0.15\/32",
+ "peerId": "192.168.0.1",
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.31.0.20/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172.31.0.20",
+ "prefixLen": 32,
+ "network": "172.31.0.20\/32",
+ "peerId": "192.168.0.1",
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.31.0.111/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172.31.0.111",
+ "prefixLen": 32,
+ "network": "172.31.0.111\/32",
+ "peerId": "192.168.0.1",
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "192.0.2.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "192.0.2.0",
+ "prefixLen": 24,
+ "network": "192.0.2.0\/24",
+ "peerId": "192.168.0.1",
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "192.168.255.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "192.168.255.0",
+ "prefixLen": 24,
+ "network": "192.168.255.0\/24",
+ "peerId": "192.168.0.1",
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "444:2":
+ {
+ "10.200.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "10.200.0.0",
+ "prefixLen": 24,
+ "network": "10.200.0.0\/24",
+ "peerId": "(unspec)",
+ "nhVrfName": "vrf1",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgpd.conf
new file mode 100644
index 0000000..5fb7902
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgpd.conf
@@ -0,0 +1,25 @@
+router bgp 65501
+ bgp router-id 192.168.0.2
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.1 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 192.168.0.1 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 192.168.0.1 activate
+ exit-address-family
+!
+router bgp 65501 vrf vrf1
+ bgp router-id 192.168.0.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_per_nexthop_label/r2/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/zebra.conf
new file mode 100644
index 0000000..b7283a3
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/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 192.168.0.2/24
+!
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/bgpd.conf
new file mode 100644
index 0000000..ff32314
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/bgpd.conf
@@ -0,0 +1,13 @@
+router bgp 65500
+ bgp router-id 100.100.100.100
+ no bgp network import-check
+ neighbor 192.0.2.1 remote-as 65500
+ neighbor 192.0.2.11 remote-as 65500
+ neighbor 192.0.2.12 remote-as 65500
+ address-family ipv4 unicast
+ neighbor 192.0.2.1 route-reflector-client
+ neighbor 192.0.2.11 route-reflector-client
+ neighbor 192.0.2.12 route-reflector-client
+ exit-address-family
+!
+
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/zebra.conf
new file mode 100644
index 0000000..315c22a
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/zebra.conf
@@ -0,0 +1,4 @@
+log stdout
+interface rr-eth0
+ ip address 192.0.2.100/24
+!
diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py
new file mode 100644
index 0000000..ce278ed
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py
@@ -0,0 +1,806 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# test_bgp_vpnv4_per_nexthop_label.py
+#
+# Copyright 2023 6WIND S.A.
+#
+
+"""
+ test_bgp_vpnv4_per_nexthop_label.py: Test the FRR BGP daemon using EBGP peering
+ Let us exchange VPNv4 updates between both devices
+ Updates from r1 will originate from the same RD, but will have separate
+ label values.
+
+ +----------+
+ | r11 |
+ |192.0.2.11+---+
+ | | | +----+--------+ +----------+
+ +----------+ | 192.0.2.1 |vrf | r1 |192.168.0.0/24| r2 |
+ +-------------------+ | 1+--------------+ |
+ +----------+ | |VRF1|AS65500 | | AS65501 |
+ | r12 | | +-------------+ | VPNV4| |VPNV4 |
+ |192.0.2.12+---+ |192.168.255.1+-+--+--------+ +----------+
+ | | |
+ +----------+ |
+ |
+ +----------+ |
+ | r13 | |
+ |192.168. +---------+
+ | 255.13 |
+ +----------+
+"""
+
+import os
+import sys
+import json
+from functools import partial
+import pytest
+import functools
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+
+pytestmark = [pytest.mark.bgpd]
+
+PREFIXES_R11 = ["172.31.0.11/32", "172.31.0.20/32", "172.31.0.111/32"]
+PREFIXES_R12 = ["172.31.0.12/32", "172.31.0.15/32"]
+PREFIXES_R13 = ["172.31.0.13/32"]
+PREFIXES_REDIST = ["172.31.0.14/32"]
+PREFIXES_CONNECTED = ["192.168.255.0/24", "192.0.2.0/24"]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 2 routers.
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+ tgen.add_router("r11")
+ tgen.add_router("r12")
+ tgen.add_router("r13")
+ tgen.add_router("r14")
+ tgen.add_router("rr")
+
+ 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["r11"])
+ switch.add_link(tgen.gears["r12"])
+ switch.add_link(tgen.gears["rr"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r13"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r14"])
+
+
+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",
+ ]
+ cmds_list_plus = [
+ "ip link set dev {0}-eth2 master vrf1",
+ ]
+
+ for cmd in cmds_list:
+ input = cmd.format("r1")
+ logger.info("input: " + cmd)
+ output = tgen.net["r1"].cmd(cmd.format("r1"))
+ logger.info("output: " + output)
+
+ for cmd in cmds_list_plus:
+ input = cmd.format("r1")
+ logger.info("input: " + cmd)
+ output = tgen.net["r1"].cmd(cmd.format("r1"))
+ logger.info("output: " + output)
+
+ for cmd in cmds_list:
+ input = cmd.format("r2")
+ logger.info("input: " + cmd)
+ output = tgen.net["r2"].cmd(cmd.format("r2"))
+ 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 bgp_vpnv4_table_check(router, group, label_list=None, label_value_expected=None):
+ """
+ Dump and check that vpnv4 entries have the same MPLS label value
+ * 'router': the router to check
+ * 'group': the list of prefixes to check. a single label value for the group has to be found
+ * 'label_list': check that the label values are not present in the vpnv4 entries
+ * that list is updated with the present label value
+ * 'label_value_expected': check that the mpls label read is the same as that value
+ """
+
+ stored_label_inited = False
+ for prefix in group:
+ dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True)
+ assert dump, "{0}, {1}, route distinguisher not present".format(
+ router.name, prefix
+ )
+ for rd, pathes in dump.items():
+ for path in pathes["paths"]:
+ assert (
+ "remoteLabel" in path.keys()
+ ), "{0}, {1}, remoteLabel not present".format(router.name, prefix)
+ logger.info(
+ "{0}, {1}, label value is {2}".format(
+ router.name, prefix, path["remoteLabel"]
+ )
+ )
+ if stored_label_inited:
+ assert (
+ path["remoteLabel"] == stored_label
+ ), "{0}, {1}, label value not expected one (expected {2}, observed {3}".format(
+ router.name, prefix, stored_label, path["remoteLabel"]
+ )
+ else:
+ stored_label = path["remoteLabel"]
+ stored_label_inited = True
+ if label_list is not None:
+ assert (
+ stored_label not in label_list
+ ), "{0}, {1}, label already detected in a previous prefix".format(
+ router.name, prefix
+ )
+ label_list.add(stored_label)
+
+ if label_value_expected:
+ assert (
+ path["remoteLabel"] == label_value_expected
+ ), "{0}, {1}, label value not expected (expected {2}, observed {3}".format(
+ router.name, prefix, label_value_expected, path["remoteLabel"]
+ )
+
+
+def bgp_vpnv4_table_check_all(router, label_list=None, same=False):
+ """
+ Dump and check that vpnv4 entries are correctly configured with specific label values
+ * 'router': the router to check
+ * 'label_list': check that the label values are not present in the vpnv4 entries
+ * that list is updated with the present label value found.
+ * 'same': by default, set to False. Addresses groups are classified by addresses.
+ * if set to True, all entries of all groups should have a unique label value
+ """
+ if same:
+ bgp_vpnv4_table_check(
+ router,
+ group=PREFIXES_R11
+ + PREFIXES_R12
+ + PREFIXES_R13
+ + PREFIXES_REDIST
+ + PREFIXES_CONNECTED,
+ label_list=label_list,
+ )
+ else:
+ for group in (
+ PREFIXES_R11,
+ PREFIXES_R12,
+ PREFIXES_R13,
+ PREFIXES_REDIST,
+ PREFIXES_CONNECTED,
+ ):
+ bgp_vpnv4_table_check(router, group=group, label_list=label_list)
+
+
+def check_show_mpls_table(router, blacklist=None, label_list=None, whitelist=None):
+ nexthop_list = []
+ if blacklist:
+ nexthop_list.append(blacklist)
+
+ dump = router.vtysh_cmd("show mpls table json", isjson=True)
+ for in_label, label_info in dump.items():
+ if label_list is not None:
+ label_list.add(in_label)
+ for nh in label_info["nexthops"]:
+ if "installed" not in nh.keys():
+ return "{} {} is not installed yet on {}".format(
+ in_label, label_info, router.name
+ )
+ if nh["installed"] != True or nh["type"] != "BGP":
+ return "{}, show mpls table, nexthop is not installed".format(
+ router.name
+ )
+ if "nexthop" in nh.keys():
+ if nh["nexthop"] in nexthop_list:
+ return "{}, show mpls table, duplicated or blacklisted nexthop address".format(
+ router.name
+ )
+ nexthop_list.append(nh["nexthop"])
+ elif "interface" in nh.keys():
+ if nh["interface"] in nexthop_list:
+ return "{}, show mpls table, duplicated or blacklisted nexthop interface".format(
+ router.name
+ )
+ nexthop_list.append(nh["interface"])
+ else:
+ return "{}, show mpls table, entry with neither nexthop nor interface".format(
+ router.name
+ )
+
+ if whitelist:
+ for entry in whitelist:
+ if entry not in nexthop_list:
+ return "{}, show mpls table, entry with nexthop {} not present in nexthop list".format(
+ router.name, entry
+ )
+ return None
+
+
+def mpls_table_check(router, blacklist=None, label_list=None, whitelist=None):
+ """
+ Dump and check 'show mpls table json' output. An assert is triggered in case test fails
+ * 'router': the router to check
+ * 'blacklist': the list of nexthops (IP or interface) that should not be on output
+ * 'label_list': the list of labels that should be in inLabel value
+ * 'whitelist': the list of nexthops (IP or interface) that should be on output
+ """
+ logger.info("Checking MPLS labels on {}".format(router.name))
+ # Check r2 removed 172.31.0.30 vpnv4 update
+ test_func = functools.partial(
+ check_show_mpls_table, router, blacklist, label_list, whitelist
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "{}, MPLS labels check fail: {}".format(router.name, result)
+
+
+def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None):
+ output = json.loads(
+ router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix))
+ )
+ if label:
+ expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}}
+ else:
+ expected = {rd: {"prefix": prefix}}
+ ret = topotest.json_cmp(output, expected)
+ if ret is None:
+ return "not good"
+ return None
+
+
+def check_show_bgp_vpn_prefix_found(router, ipversion, prefix, rd):
+ output = json.loads(
+ router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix))
+ )
+ expected = {rd: {"prefix": prefix}}
+ return topotest.json_cmp(output, expected)
+
+
+def check_show_mpls_table_entry_label_found(router, inlabel, interface):
+ output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel)))
+ expected = {
+ "inLabel": inlabel,
+ "installed": True,
+ "nexthops": [{"interface": interface}],
+ }
+ return topotest.json_cmp(output, expected)
+
+
+def check_show_mpls_table_entry_label_not_found(router, inlabel):
+ output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel)))
+ expected = {"inlabel": inlabel, "installed": True}
+ ret = topotest.json_cmp(output, expected)
+ if ret is None:
+ return "not good"
+ return None
+
+
+def mpls_entry_get_interface(router, label):
+ """
+ Assert that the label is in MPLS table
+ Assert an outgoing interface is programmed
+ return the outgoing interface
+ """
+ outgoing_interface = None
+
+ logger.info("Checking MPLS labels on {}".format(router.name))
+ dump = router.vtysh_cmd("show mpls table {} json".format(label), isjson=True)
+ assert dump, "{0}, label {1} not present".format(router.name, label)
+
+ for nh in dump["nexthops"]:
+ assert (
+ "interface" in nh.keys()
+ ), "{}, show mpls table, nexthop interface not present for MPLS entry {}".format(
+ router.name, label
+ )
+
+ outgoing_interface = nh["interface"]
+
+ return outgoing_interface
+
+
+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 BGP IPv4 routing tables on VRF1 of r1
+ logger.info("Checking BGP IPv4 routes for convergence on r1 VRF1")
+ router = tgen.gears["r1"]
+ json_file = "{}/{}/bgp_ipv4_routes_vrf1.json".format(CWD, router.name)
+ 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=20, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ logger.info("Checking BGP VPNv4 routes for convergence on r2")
+ router = tgen.gears["r2"]
+ json_file = "{}/{}/bgp_vpnv4_routes.json".format(CWD, router.name)
+ 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 labels received on r2
+ logger.info("Checking BGP VPNv4 labels on r2")
+ label_list = set()
+ bgp_vpnv4_table_check_all(tgen.gears["r2"], label_list)
+
+ # Check MPLS labels received on r1
+ mpls_table_check(tgen.gears["r1"], label_list)
+
+
+def test_flapping_bgp_vrf_down():
+ """
+ Turn down a remote BGP session
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ logger.info("Unpeering BGP on r11")
+ tgen.gears["r11"].vtysh_cmd(
+ "configure terminal\nrouter bgp 65500\nno neighbor 192.0.2.100\n",
+ isjson=False,
+ )
+
+ def _bgp_prefix_not_found(router, vrf, ipversion, prefix):
+ output = json.loads(
+ router.vtysh_cmd(
+ "show bgp vrf {} {} {} json".format(vrf, ipversion, prefix)
+ )
+ )
+ expected = {"prefix": prefix}
+ ret = topotest.json_cmp(output, expected)
+ if ret is None:
+ return "not good"
+ return None
+
+ # Check prefix from r11 is not present
+ test_func = functools.partial(
+ _bgp_prefix_not_found, tgen.gears["r1"], "vrf1", "ipv4", "172.31.0.11/32"
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert (
+ success
+ ), "r1, prefix 172.31.0.11/32 from r11 did not disappear. r11 still connected to rr ?"
+
+ # Check BGP updated received on r2 are not from r11
+ logger.info("Checking BGP VPNv4 labels on r2")
+ for entry in PREFIXES_R11:
+ dump = tgen.gears["r2"].vtysh_cmd(
+ "show bgp ipv4 vpn {} json".format(entry), isjson=True
+ )
+ for rd in dump:
+ assert False, "r2, {}, route distinguisher {} present".format(entry, rd)
+
+ mpls_table_check(tgen.gears["r1"], blacklist=["192.0.2.11"])
+
+
+def test_flapping_bgp_vrf_up():
+ """
+ Turn up a remote BGP session
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ logger.info("Peering BGP on r11")
+ tgen.gears["r11"].vtysh_cmd(
+ "configure terminal\nrouter bgp 65500\nneighbor 192.0.2.100 remote-as 65500\n",
+ isjson=False,
+ )
+
+ # Check r2 gets prefix 172.31.0.11/128
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_found,
+ tgen.gears["r2"],
+ "ipv4",
+ "172.31.0.11/32",
+ "444:1",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert (
+ success
+ ), "r2, prefix 172.31.0.11/32 from r11 not present. r11 still disconnected from rr ?"
+ bgp_vpnv4_table_check_all(tgen.gears["r2"])
+
+
+def test_recursive_route():
+ """
+ Test static recursive route redistributed over BGP
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Enabling recursive static route")
+ tgen.gears["r1"].vtysh_cmd(
+ "configure terminal\nvrf vrf1\nip route 172.31.0.30/32 172.31.0.20\n",
+ isjson=False,
+ )
+ logger.info("Checking BGP VPNv4 labels on r2")
+
+ # Check r2 received vpnv4 update with 172.31.0.30
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_found,
+ tgen.gears["r2"],
+ "ipv4",
+ "172.31.0.30/32",
+ "444:1",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r2, vpnv4 update 172.31.0.30 not found"
+
+ bgp_vpnv4_table_check(tgen.gears["r2"], group=PREFIXES_R11 + ["172.31.0.30/32"])
+
+ # diagnostic
+ logger.info("Dumping label nexthop table")
+ tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False)
+ logger.info("Dumping nexthop table")
+ tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 nexthop detail", isjson=False)
+
+ logger.info("Disabling recursive static route")
+ tgen.gears["r1"].vtysh_cmd(
+ "configure terminal\nvrf vrf1\nno ip route 172.31.0.30/32 172.31.0.20\n",
+ isjson=False,
+ )
+ logger.info("Checking BGP VPNv4 labels on r2")
+
+ # Check r2 removed 172.31.0.30 vpnv4 update
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_not_found,
+ tgen.gears["r2"],
+ "ipv4",
+ "172.31.0.30/32",
+ "444:1",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r2, vpnv4 update 172.31.0.30 still present"
+
+
+def test_prefix_changes_interface():
+ """
+ Test BGP update for a given prefix learnt on different interface
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Enabling a 172.31.0.50/32 prefix for r11")
+ tgen.gears["r11"].vtysh_cmd(
+ "configure terminal\nrouter bgp\naddress-family ipv4 unicast\nnetwork 172.31.0.50/32",
+ isjson=False,
+ )
+
+ # Check r2 received vpnv4 update with 172.31.0.50
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_found,
+ tgen.gears["r2"],
+ "ipv4",
+ "172.31.0.50/32",
+ "444:1",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r2, vpnv4 update 172.31.0.50 not found"
+
+ # diagnostic
+ logger.info("Dumping label nexthop table")
+ tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False)
+
+ label_list = set()
+ bgp_vpnv4_table_check(
+ tgen.gears["r2"],
+ group=["172.31.0.11/32", "172.31.0.111/32", "172.31.0.50/32"],
+ label_list=label_list,
+ )
+
+ assert (
+ len(label_list) == 1
+ ), "Multiple Label values found for updates from r11 found"
+
+ oldlabel = label_list.pop()
+ logger.info("r1, getting the outgoing interface used by label {}".format(oldlabel))
+ old_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], oldlabel)
+ logger.info(
+ "r1, outgoing interface used by label {} is {}".format(
+ oldlabel, old_outgoing_interface
+ )
+ )
+
+ logger.info("Moving the 172.31.0.50/32 prefix from r11 to r13")
+ tgen.gears["r11"].vtysh_cmd(
+ "configure terminal\nrouter bgp\naddress-family ipv4 unicast\nno network 172.31.0.50/32",
+ isjson=False,
+ )
+ tgen.gears["r13"].vtysh_cmd(
+ "configure terminal\nrouter bgp\naddress-family ipv4 unicast\nnetwork 172.31.0.50/32",
+ isjson=False,
+ )
+
+ # Check r2 removed 172.31.0.50 vpnv4 update with old label
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_not_found,
+ tgen.gears["r2"],
+ "ipv4",
+ "172.31.0.50/32",
+ "444:1",
+ label=oldlabel,
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert (
+ success
+ ), "r2, vpnv4 update 172.31.0.50 with old label {0} still present".format(oldlabel)
+
+ # diagnostic
+ logger.info("Dumping label nexthop table")
+ tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False)
+
+ # Check r2 received new 172.31.0.50 vpnv4 update
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_found,
+ tgen.gears["r2"],
+ "ipv4",
+ "172.31.0.50/32",
+ "444:1",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r2, vpnv4 update 172.31.0.50 not found"
+
+ label_list = set()
+ bgp_vpnv4_table_check(
+ tgen.gears["r2"],
+ group=PREFIXES_R13 + ["172.31.0.50/32"],
+ label_list=label_list,
+ )
+ assert (
+ len(label_list) == 1
+ ), "Multiple Label values found for updates from r13 found"
+
+ newlabel = label_list.pop()
+ logger.info("r1, getting the outgoing interface used by label {}".format(newlabel))
+ new_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], newlabel)
+ logger.info(
+ "r1, outgoing interface used by label {} is {}".format(
+ newlabel, new_outgoing_interface
+ )
+ )
+ if old_outgoing_interface == new_outgoing_interface:
+ assert 0, "r1, outgoing interface did not change whereas BGP update moved"
+
+ logger.info("Restoring state by removing the 172.31.0.50/32 prefix from r13")
+ tgen.gears["r13"].vtysh_cmd(
+ "configure terminal\nrouter bgp\naddress-family ipv4 unicast\nno network 172.31.0.50/32",
+ isjson=False,
+ )
+
+
+def test_changing_default_label_value():
+ """
+ Change the MPLS default value
+ Check that r1 VPNv4 entries have the 222 label value
+ Check that MPLS entry with old label value is no more present
+ Check that MPLS entry for local traffic has inLabel set to 222
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+
+ # counting the number of labels used in the VPNv4 table
+ label_list = set()
+ logger.info("r1, vpnv4 table, check the number of labels used before modification")
+ bgp_vpnv4_table_check_all(router, label_list)
+ old_len = len(label_list)
+ assert (
+ old_len != 1
+ ), "r1, number of labels used should be greater than 1, oberved {} ".format(old_len)
+
+ logger.info("r1, vrf1, changing the default MPLS label value to export to 222")
+ router.vtysh_cmd(
+ "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nlabel vpn export 222\n",
+ isjson=False,
+ )
+
+ # Check r1 updated the MPLS entry with the 222 label value
+ logger.info(
+ "r1, mpls table, check that MPLS entry with inLabel set to 222 has vrf1 interface"
+ )
+ test_func = functools.partial(
+ check_show_mpls_table_entry_label_found, router, 222, "vrf1"
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r1, mpls entry with label 222 not found"
+
+ # check label repartition is ok
+ logger.info("r1, vpnv4 table, check the number of labels used after modification")
+ label_list = set()
+ bgp_vpnv4_table_check_all(router, label_list)
+ new_len = len(label_list)
+ assert (
+ old_len == new_len
+ ), "r1, number of labels after modification differ from previous, observed {}, expected {} ".format(
+ new_len, old_len
+ )
+
+ logger.info(
+ "r1, vpnv4 table, check that prefixes that were using the vrf label have refreshed the label value to 222"
+ )
+ bgp_vpnv4_table_check(
+ router, group=["192.168.255.0/24", "192.0.2.0/24"], label_value_expected=222
+ )
+
+
+def test_unconfigure_allocation_mode_nexthop():
+ """
+ Test unconfiguring allocation mode per nexthop
+ Check that show mpls table has no entry with label 17 (previously used)
+ Check that all VPN updates on r1 should have label value moved to 222
+ Check that show mpls table will only have 222 label value
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Unconfiguring allocation mode per nexthop")
+ router = tgen.gears["r1"]
+ router.vtysh_cmd(
+ "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nno label vpn export allocation-mode per-nexthop\n",
+ isjson=False,
+ )
+
+ # Check r1 updated the MPLS entry with the 222 label value
+ logger.info(
+ "r1, mpls table, check that MPLS entry with inLabel set to 17 is not present"
+ )
+ test_func = functools.partial(
+ check_show_mpls_table_entry_label_not_found, router, 17
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r1, mpls entry with label 17 still present"
+
+ # Check vpnv4 routes from r1
+ logger.info("Checking vpnv4 routes on r1")
+ label_list = set()
+ bgp_vpnv4_table_check_all(router, label_list=label_list, same=True)
+ assert len(label_list) == 1, "r1, multiple Label values found for vpnv4 updates"
+
+ new_label = label_list.pop()
+ assert (
+ new_label == 222
+ ), "r1, wrong label value in VPNv4 table, expected 222, observed {}".format(
+ new_label
+ )
+
+ # Check mpls table with 222 value
+ logger.info("Checking MPLS values on show mpls table of r1")
+ label_list = set()
+ label_list.add(222)
+ mpls_table_check(router, label_list=label_list)
+
+
+def test_reconfigure_allocation_mode_nexthop():
+ """
+ Test re-configuring allocation mode per nexthop
+ Check that show mpls table has no entry with label 17
+ Check that all VPN updates on r1 should have multiple label values and not only 222
+ Check that show mpls table will have multiple label values and not only 222
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Reconfiguring allocation mode per nexthop")
+ router = tgen.gears["r1"]
+ router.vtysh_cmd(
+ "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nlabel vpn export allocation-mode per-nexthop\n",
+ isjson=False,
+ )
+
+ # Check that show mpls table has no entry with label 17
+ logger.info(
+ "r1, mpls table, check that MPLS entry with inLabel set to 17 is present"
+ )
+ test_func = functools.partial(
+ check_show_mpls_table_entry_label_not_found, router, 17
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r1, mpls entry with label 17 still present"
+
+ # Check vpnv4 routes from r1
+ logger.info("Checking vpnv4 routes on r1")
+ label_list = set()
+ bgp_vpnv4_table_check_all(router, label_list=label_list)
+ assert len(label_list) != 1, "r1, only 1 label values found for vpnv4 updates"
+
+ # Check mpls table with all values
+ logger.info("Checking MPLS values on show mpls table of r1")
+ mpls_table_check(router, label_list=label_list)
+
+
+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_vpnv6_per_nexthop_label/__init__.py b/tests/topotests/bgp_vpnv6_per_nexthop_label/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/__init__.py
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json
new file mode 100644
index 0000000..39ba7dd
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json
@@ -0,0 +1,186 @@
+{
+ "vrfName": "vrf1",
+ "localAS": 65500,
+ "routes":
+ {
+ "10:200::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "10:200::",
+ "prefixLen": 64,
+ "network": "10:200::/64",
+ "nexthops": [
+ {
+ "ip": "192:168::2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172:31::11/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172:31::11",
+ "prefixLen":128,
+ "network":"172:31::11/128",
+ "peerId":"192:2::100",
+ "nexthops":[
+ {
+ "ip":"192:2::11",
+ "afi":"ipv6",
+ "scope":"global"
+ }
+ ]
+ }
+ ],
+ "172:31::12/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172:31::12",
+ "prefixLen":128,
+ "network":"172:31::12/128",
+ "peerId":"192:2::100",
+ "nexthops":[
+ {
+ "ip":"192:2::12",
+ "afi":"ipv6",
+ "scope":"global",
+ "used":true
+ },
+ {
+ "scope": "link-local"
+ }
+ ]
+ }
+ ],
+ "172:31::13/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172:31::13",
+ "prefixLen":128,
+ "network":"172:31::13/128",
+ "peerId":"192:168::255:13",
+ "nexthops":[
+ {
+ "ip":"192:168::255:13",
+ "afi":"ipv6",
+ "scope": "global",
+ "used":true
+ },
+ {
+ "scope": "link-local"
+ }
+ ]
+ }
+ ],
+ "172:31::14/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172:31::14",
+ "prefixLen":128,
+ "network":"172:31::14/128",
+ "peerId":"(unspec)",
+ "nexthops":[
+ {
+ "ip":"192:2::14",
+ "afi":"ipv6",
+ "used":true
+ }
+ ]
+ }
+ ],
+ "172:31::15/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172:31::15",
+ "prefixLen":128,
+ "network":"172:31::15/128",
+ "peerId":"(unspec)",
+ "nexthops":[
+ {
+ "ip":"192:2::12",
+ "afi":"ipv6",
+ "used":true
+ }
+ ]
+ }
+ ],
+ "172:31::20/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172:31::20",
+ "prefixLen":128,
+ "network":"172:31::20/128",
+ "peerId":"192:2::100",
+ "nexthops":[
+ {
+ "ip":"192:2::11",
+ "afi":"ipv6",
+ "scope":"global",
+ "used":true
+ }
+ ]
+ }
+ ],
+ "172:31::111/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"172:31::111",
+ "prefixLen":128,
+ "network":"172:31::111/128",
+ "peerId":"192:2::100",
+ "nexthops":[
+ {
+ "ip":"192:2::11",
+ "afi":"ipv6",
+ "scope":"global",
+ "used":true
+ }
+ ]
+ }
+ ],
+ "192:2::/64": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"192:2::",
+ "prefixLen":64,
+ "network":"192:2::/64",
+ "peerId":"(unspec)",
+ "nexthops":[
+ {
+ "ip":"::",
+ "afi":"ipv6",
+ "used":true
+ }
+ ]
+ }
+ ],
+ "192:168::255:0/112": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "prefix":"192:168::255:0",
+ "prefixLen":112,
+ "network":"192:168::255:0/112",
+ "peerId":"(unspec)",
+ "nexthops":[
+ {
+ "ip":"::",
+ "afi":"ipv6",
+ "used":true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf
new file mode 100644
index 0000000..6bb0a88
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf
@@ -0,0 +1,45 @@
+debug bgp vpn leak-from-vrf
+debug bgp vpn label
+debug bgp nht
+debug bgp updates out
+router bgp 65500
+ bgp router-id 192.168.0.1
+ no bgp ebgp-requires-policy
+ neighbor 192:168::2 remote-as 65501
+ address-family ipv4 unicast
+ no neighbor 192:168::2 activate
+ exit-address-family
+ address-family ipv6 vpn
+ neighbor 192:168::2 activate
+ neighbor 192:168::2 soft-reconfiguration inbound
+ exit-address-family
+!
+router bgp 65500 vrf vrf1
+ bgp router-id 192.168.0.1
+ neighbor 192:2::100 remote-as 65500
+ neighbor 192:168::255:13 remote-as 65500
+ address-family ipv6 unicast
+ neighbor 192:2::100 activate
+ neighbor 192:2::100 route-map rmap in
+ neighbor 192:168::255:13 activate
+ neighbor 192:168::255:13 route-map rmap in
+ redistribute connected
+ redistribute static route-map rmap
+ label vpn export allocation-mode per-nexthop
+ label vpn export auto
+ rd vpn export 444:1
+ rt vpn both 52:100
+ export vpn
+ import vpn
+ exit-address-family
+!
+interface r1-eth0
+ mpls bgp forwarding
+!
+bgp community-list 1 seq 5 permit 10:10
+!
+route-map rmap permit 1
+ set ipv6 next-hop prefer-global
+!
+route-map rmap permit 2
+!
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/zebra.conf
new file mode 100644
index 0000000..bdad9ee
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/zebra.conf
@@ -0,0 +1,18 @@
+log stdout
+debug zebra nht
+!debug zebra kernel msgdump recv
+!debug zebra dplane detailed
+!debug zebra packet recv
+interface r1-eth1 vrf vrf1
+ ipv6 address 192:2::1/64
+!
+interface r1-eth2 vrf vrf1
+ ipv6 address 192:168::255:1/112
+!
+interface r1-eth0
+ ip address 192:168::1/112
+!
+vrf vrf1
+ ipv6 route 172:31::14/128 192:2::14
+ ipv6 route 172:31::15/128 192:2::12
+exit-vrf
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf
new file mode 100644
index 0000000..cb653d6
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf
@@ -0,0 +1,15 @@
+router bgp 65500
+ bgp router-id 11.11.11.11
+ no bgp network import-check
+ neighbor 192:2::100 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 192:2::100 activate
+ !
+ address-family ipv6 unicast
+ neighbor 192:2::100 activate
+ network 172:31::11/128
+ network 172:31::111/128
+ network 172:31::20/128
+ exit-address-family
+!
+
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/zebra.conf
new file mode 100644
index 0000000..a76080d
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/zebra.conf
@@ -0,0 +1,4 @@
+log stdout
+interface r11-eth0
+ ipv6 address 192:2::11/64
+!
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/bgpd.conf
new file mode 100644
index 0000000..d41fb18
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/bgpd.conf
@@ -0,0 +1,13 @@
+router bgp 65500
+ bgp router-id 12.12.12.12
+ no bgp network import-check
+ neighbor 192:2::100 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 192:2::100 activate
+ !
+ address-family ipv6 unicast
+ neighbor 192:2::100 activate
+ network 172:31::12/128
+ exit-address-family
+!
+
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/zebra.conf
new file mode 100644
index 0000000..df9cae4
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/zebra.conf
@@ -0,0 +1,4 @@
+log stdout
+interface r12-eth0
+ ipv6 address 192:2::12/64
+!
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf
new file mode 100644
index 0000000..0437882
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf
@@ -0,0 +1,12 @@
+router bgp 65500
+ bgp router-id 13.13.13.13
+ no bgp network import-check
+ neighbor 192:168::255:1 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 192:168::255:1 activate
+ exit-address-family
+ address-family ipv6 unicast
+ neighbor 192:168::255:1 activate
+ network 172:31::0:13/128
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/zebra.conf
new file mode 100644
index 0000000..dfe5994
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/zebra.conf
@@ -0,0 +1,4 @@
+log stdout
+interface r13-eth0
+ ipv6 address 192:168::255:13/112
+!
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgp_vpnv6_routes.json b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgp_vpnv6_routes.json
new file mode 100644
index 0000000..bb7d5c0
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgp_vpnv6_routes.json
@@ -0,0 +1,187 @@
+{
+ "vrfName": "default",
+ "localAS": 65501,
+ "routes":
+ {
+ "routeDistinguishers":
+ {
+ "444:1":
+ {
+ "172:31::11/128": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172:31::11",
+ "prefixLen": 128,
+ "network": "172:31::11/128",
+ "peerId": "192:168::1",
+ "nexthops": [
+ {
+ "ip": "192:168::1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172:31::12/128": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172:31::12",
+ "prefixLen": 128,
+ "network": "172:31::12/128",
+ "peerId": "192:168::1",
+ "nexthops": [
+ {
+ "ip": "192:168::1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172:31::13/128": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172:31::13",
+ "prefixLen": 128,
+ "network": "172:31::13/128",
+ "peerId": "192:168::1",
+ "nexthops": [
+ {
+ "ip": "192:168::1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172:31::14/128": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172:31::14",
+ "prefixLen": 128,
+ "network": "172:31::14/128",
+ "peerId": "192:168::1",
+ "nexthops": [
+ {
+ "ip": "192:168::1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172:31::15/128": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172:31::15",
+ "prefixLen": 128,
+ "network": "172:31::15/128",
+ "peerId": "192:168::1",
+ "nexthops": [
+ {
+ "ip": "192:168::1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172:31::20/128": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172:31::20",
+ "prefixLen": 128,
+ "network": "172:31::20/128",
+ "peerId": "192:168::1",
+ "nexthops": [
+ {
+ "ip": "192:168::1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172:31::111/128": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "172:31::111",
+ "prefixLen": 128,
+ "network": "172:31::111/128",
+ "peerId": "192:168::1",
+ "nexthops": [
+ {
+ "ip": "192:168::1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "192:2::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "192:2::",
+ "prefixLen": 64,
+ "network": "192:2::/64",
+ "peerId": "192:168::1",
+ "nexthops": [
+ {
+ "ip": "192:168::1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "192:168::255:0/112": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "192:168::255:0",
+ "prefixLen": 112,
+ "network": "192:168::255:0/112",
+ "peerId": "192:168::1",
+ "nexthops": [
+ {
+ "ip": "192:168::1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "444:2":
+ {
+ "10:200::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "10:200::",
+ "prefixLen": 64,
+ "network": "10:200::/64",
+ "peerId": "(unspec)",
+ "nhVrfName": "vrf1",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgpd.conf
new file mode 100644
index 0000000..30e9959
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgpd.conf
@@ -0,0 +1,25 @@
+router bgp 65501
+ bgp router-id 192.168.0.2
+ no bgp ebgp-requires-policy
+ neighbor 192:168::1 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 192:168::1 activate
+ exit-address-family
+ address-family ipv6 vpn
+ neighbor 192:168::1 activate
+ exit-address-family
+!
+router bgp 65501 vrf vrf1
+ bgp router-id 192.168.0.2
+ address-family ipv6 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_vpnv6_per_nexthop_label/r2/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/zebra.conf
new file mode 100644
index 0000000..47cee95
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/zebra.conf
@@ -0,0 +1,7 @@
+log stdout
+interface r2-eth1 vrf vrf1
+ ipv6 address 10:200::2/64
+!
+interface r2-eth0
+ ipv6 address 192:168::2/112
+!
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/bgpd.conf
new file mode 100644
index 0000000..8c7664b
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/bgpd.conf
@@ -0,0 +1,24 @@
+router bgp 65500
+ bgp router-id 100.100.100.100
+ no bgp network import-check
+ neighbor 192:2::1 remote-as 65500
+ neighbor 192:2::11 remote-as 65500
+ neighbor 192:2::12 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 192:2::1 activate
+ no neighbor 192:2::11 activate
+ no neighbor 192:2::12 activate
+ !
+ address-family ipv6 unicast
+ neighbor 192:2::1 activate
+ neighbor 192:2::1 route-reflector-client
+ neighbor 192:2::1 nexthop-local unchanged
+ neighbor 192:2::11 activate
+ neighbor 192:2::11 route-reflector-client
+ neighbor 192:2::11 nexthop-local unchanged
+ neighbor 192:2::12 activate
+ neighbor 192:2::12 route-reflector-client
+ neighbor 192:2::12 nexthop-local unchanged
+ exit-address-family
+!
+
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/zebra.conf
new file mode 100644
index 0000000..94b82dc
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/zebra.conf
@@ -0,0 +1,4 @@
+log stdout
+interface rr-eth0
+ ipv6 address 192:2::100/64
+!
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py
new file mode 100644
index 0000000..e936ccc
--- /dev/null
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py
@@ -0,0 +1,808 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# test_bgp_vpnv6_per_nexthop_label.py
+#
+# Copyright 2023 6WIND S.A.
+#
+
+"""
+ test_bgp_vpnv6_per_nexthop_label.py: Test the FRR BGP daemon using EBGP peering
+ Let us exchange VPNv6 updates between both devices
+ Updates from r1 will originate from the same RD, but will have separate
+ label values.
+
+ +----------+
+ | r11 |
+ |192::2:11 +---+
+ | | | +----+--------+ +----------+
+ +----------+ | 192::2::1 |vrf | r1 |192:168::/112 | r2 |
+ +-------------------+ | 1+--------------+ |
+ +----------+ | |VRF1|AS65500 | | AS65501 |
+ | r12 | | +--------------+ | VPNV4| |VPNV4 |
+ |192::2:12 +---+ |192:168::255:1+-+--+--------+ +----------+
+ | | |
+ +----------+ |
+ |
+ +----------+ |
+ | r13 | |
+ |192:168:: +--------+
+ | 255:13 |
+ +----------+
+"""
+
+import os
+import sys
+import json
+from functools import partial
+import pytest
+import functools
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+
+pytestmark = [pytest.mark.bgpd]
+
+PREFIXES_R11 = ["172:31::11/128", "172:31::20/128", "172:31::111/128"]
+PREFIXES_R12 = ["172:31::12/128", "172:31::15/128"]
+PREFIXES_REDIST_R14 = ["172:31::14/128"]
+PREFIXES_CONNECTED = ["192:168::255/112", "192:2::/64"]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 2 routers.
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+ tgen.add_router("r11")
+ tgen.add_router("r12")
+ tgen.add_router("r13")
+ tgen.add_router("r14")
+ tgen.add_router("rr")
+
+ 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["r11"])
+ switch.add_link(tgen.gears["r12"])
+ switch.add_link(tgen.gears["rr"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r13"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r14"])
+
+
+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",
+ ]
+ cmds_list_plus = [
+ "ip link set dev {0}-eth2 master vrf1",
+ ]
+
+ for cmd in cmds_list:
+ input = cmd.format("r1")
+ logger.info("input: " + cmd)
+ output = tgen.net["r1"].cmd(cmd.format("r1"))
+ logger.info("output: " + output)
+
+ for cmd in cmds_list_plus:
+ input = cmd.format("r1")
+ logger.info("input: " + cmd)
+ output = tgen.net["r1"].cmd(cmd.format("r1"))
+ logger.info("output: " + output)
+
+ for cmd in cmds_list:
+ input = cmd.format("r2")
+ logger.info("input: " + cmd)
+ output = tgen.net["r2"].cmd(cmd.format("r2"))
+ 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 bgp_vpnv6_table_check(router, group, label_list=None, label_value_expected=None):
+ """
+ Dump and check that vpnv6 entries have the same MPLS label value
+ * 'router': the router to check
+ * 'group': the list of prefixes to check. a single label value for the group has to be found
+ * 'label_list': check that the label values are not present in the vpnv6 entries
+ * that list is updated with the present label value
+ * 'label_value_expected': check that the mpls label read is the same as that value
+ """
+
+ stored_label_inited = False
+ for prefix in group:
+ dump = router.vtysh_cmd("show bgp ipv6 vpn {} json".format(prefix), isjson=True)
+ for rd, pathes in dump.items():
+ for path in pathes["paths"]:
+ assert (
+ "remoteLabel" in path.keys()
+ ), "{0}, {1}, remoteLabel not present".format(router.name, prefix)
+ logger.info(
+ "{0}, {1}, label value is {2}".format(
+ router.name, prefix, path["remoteLabel"]
+ )
+ )
+ if stored_label_inited:
+ assert (
+ path["remoteLabel"] == stored_label
+ ), "{0}, {1}, label value not expected one (expected {2}, observed {3}".format(
+ router.name, prefix, stored_label, path["remoteLabel"]
+ )
+ else:
+ stored_label = path["remoteLabel"]
+ stored_label_inited = True
+ if label_list is not None:
+ assert (
+ stored_label not in label_list
+ ), "{0}, {1}, label already detected in a previous prefix".format(
+ router.name, prefix
+ )
+ label_list.add(stored_label)
+
+ if label_value_expected:
+ assert (
+ path["remoteLabel"] == label_value_expected
+ ), "{0}, {1}, label value not expected (expected {2}, observed {3}".format(
+ router.name, prefix, label_value_expected, path["remoteLabel"]
+ )
+
+
+def bgp_vpnv6_table_check_all(router, label_list=None, same=False):
+ """
+ Dump and check that vpnv6 entries are correctly configured with specific label values
+ * 'router': the router to check
+ * 'label_list': check that the label values are not present in the vpnv6 entries
+ * that list is updated with the present label value found.
+ * 'same': by default, set to False. Addresses groups are classified by addresses.
+ * if set to True, all entries of all groups should have a unique label value
+ """
+ if same:
+ bgp_vpnv6_table_check(
+ router,
+ group=PREFIXES_R11
+ + PREFIXES_R12
+ + PREFIXES_REDIST_R14
+ + PREFIXES_CONNECTED,
+ label_list=label_list,
+ )
+ else:
+ for group in (
+ PREFIXES_R11,
+ PREFIXES_R12,
+ PREFIXES_REDIST_R14,
+ PREFIXES_CONNECTED,
+ ):
+ bgp_vpnv6_table_check(router, group=group, label_list=label_list)
+
+
+def check_show_mpls_table(router, blacklist=None, label_list=None, whitelist=None):
+ nexthop_list = []
+ if blacklist:
+ nexthop_list.append(blacklist)
+
+ dump = router.vtysh_cmd("show mpls table json", isjson=True)
+ for in_label, label_info in dump.items():
+ if label_list is not None:
+ label_list.add(in_label)
+ for nh in label_info["nexthops"]:
+ if "installed" not in nh.keys():
+ return "{} {} is not installed yet on {}".format(in_label, label_info, router.name)
+ if nh["installed"] != True or nh["type"] != "BGP":
+ return "{}, show mpls table, nexthop is not installed".format(
+ router.name
+ )
+ if "nexthop" in nh.keys():
+ if nh["nexthop"] in nexthop_list:
+ return "{}, show mpls table, duplicated or blacklisted nexthop address".format(
+ router.name
+ )
+ nexthop_list.append(nh["nexthop"])
+ elif "interface" in nh.keys():
+ if nh["interface"] in nexthop_list:
+ return "{}, show mpls table, duplicated or blacklisted nexthop interface".format(
+ router.name
+ )
+ nexthop_list.append(nh["interface"])
+ else:
+ return "{}, show mpls table, entry with neither nexthop nor interface".format(
+ router.name
+ )
+
+ if whitelist:
+ for entry in whitelist:
+ if entry not in nexthop_list:
+ return "{}, show mpls table, entry with nexthop {} not present in nexthop list".format(
+ router.name, entry
+ )
+ return None
+
+
+def mpls_table_check(router, blacklist=None, label_list=None, whitelist=None):
+ """
+ Dump and check 'show mpls table json' output. An assert is triggered in case test fails
+ * 'router': the router to check
+ * 'blacklist': the list of nexthops (IP or interface) that should not be on output
+ * 'label_list': the list of labels that should be in inLabel value
+ * 'whitelist': the list of nexthops (IP or interface) that should be on output
+ """
+ logger.info("Checking MPLS labels on {}".format(router.name))
+ logger.info("Checking MPLS labels on {}".format(router.name))
+ # Check r2 removed 172.31.0.30 vpnv4 update
+ test_func = functools.partial(
+ check_show_mpls_table, router, blacklist, label_list, whitelist
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "{}, MPLS labels check fail: {}".format(router.name, result)
+
+
+def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None):
+ output = json.loads(
+ router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix))
+ )
+ if label:
+ expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}}
+ else:
+ expected = {rd: {"prefix": prefix}}
+ ret = topotest.json_cmp(output, expected)
+ if ret is None:
+ return "not good"
+ return None
+
+
+def check_show_bgp_vpn_prefix_found(router, ipversion, prefix, rd):
+ output = json.loads(
+ router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix))
+ )
+ expected = {rd: {"prefix": prefix}}
+ return topotest.json_cmp(output, expected)
+
+
+def check_show_mpls_table_entry_label_found(router, inlabel, interface):
+ output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel)))
+ expected = {
+ "inLabel": inlabel,
+ "installed": True,
+ "nexthops": [{"interface": interface}],
+ }
+ return topotest.json_cmp(output, expected)
+
+
+def check_show_mpls_table_entry_label_not_found(router, inlabel):
+ output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel)))
+ expected = {"inlabel": inlabel, "installed": True}
+ ret = topotest.json_cmp(output, expected)
+ if ret is None:
+ return "not good"
+ return None
+
+
+def mpls_entry_get_interface(router, label):
+ """
+ Assert that the label is in MPLS table
+ Assert an outgoing interface is programmed
+ return the outgoing interface
+ """
+ outgoing_interface = None
+
+ logger.info("Checking MPLS labels on {}".format(router.name))
+ dump = router.vtysh_cmd("show mpls table {} json".format(label), isjson=True)
+ assert dump, "{}, show mpls table, inLabel {} not found".format(router.name, label)
+
+ for nh in dump["nexthops"]:
+ assert (
+ "interface" in nh.keys()
+ ), "{}, show mpls table, nexthop interface not present for MPLS entry {}".format(
+ router.name, label
+ )
+
+ outgoing_interface = nh["interface"]
+
+ return outgoing_interface
+
+
+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 BGP IPv6 routing tables on VRF1 of r1
+ logger.info("Checking BGP IPv6 routes for convergence on r1 VRF1")
+ router = tgen.gears["r1"]
+ json_file = "{}/{}/bgp_ipv6_routes_vrf1.json".format(CWD, router.name)
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show bgp vrf vrf1 ipv6 json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ logger.info("Checking BGP VPNv6 routes for convergence on r2")
+ router = tgen.gears["r2"]
+ json_file = "{}/{}/bgp_vpnv6_routes.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show bgp ipv6 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 labels received on r2
+ logger.info("Checking BGP VPNv6 labels on r2")
+ label_list = set()
+ bgp_vpnv6_table_check_all(tgen.gears["r2"], label_list)
+
+ # Check MPLS labels received on r1
+ mpls_table_check(tgen.gears["r1"], label_list)
+
+
+def test_flapping_bgp_vrf_down():
+ """
+ Turn down a remote BGP session
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ logger.info("Unpeering BGP on r11")
+ tgen.gears["r11"].vtysh_cmd(
+ "configure terminal\nrouter bgp 65500\nno neighbor 192:2::100\n",
+ isjson=False,
+ )
+
+ def _bgp_prefix_not_found(router, vrf, ipversion, prefix):
+ output = json.loads(
+ router.vtysh_cmd(
+ "show bgp vrf {} {} {} json".format(vrf, ipversion, prefix)
+ )
+ )
+ expected = {"prefix": prefix}
+ ret = topotest.json_cmp(output, expected)
+ if ret is None:
+ return "not good"
+ return None
+
+ # Check prefix from r11 is not present
+ test_func = functools.partial(
+ _bgp_prefix_not_found, tgen.gears["r1"], "vrf1", "ipv6", "172:31::11/128"
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert (
+ success
+ ), "r1, prefix 172:31::11/128 from r11 did not disappear. r11 still connected to rr ?"
+
+ # Check BGP updated received on r2 are not from r11
+ logger.info("Checking BGP VPNv6 labels on r2")
+ for entry in PREFIXES_R11:
+ dump = tgen.gears["r2"].vtysh_cmd(
+ "show bgp ipv6 vpn {} json".format(entry), isjson=True
+ )
+ for rd in dump:
+ assert False, "r2, {}, route distinguisher {} present".format(entry, rd)
+
+ mpls_table_check(tgen.gears["r1"], blacklist=["192:2::11"])
+
+
+def test_flapping_bgp_vrf_up():
+ """
+ Turn up a remote BGP session
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ logger.info("Peering BGP on r11")
+ tgen.gears["r11"].vtysh_cmd(
+ "configure terminal\nrouter bgp 65500\nneighbor 192:2::100 remote-as 65500\n",
+ isjson=False,
+ )
+ tgen.gears["r11"].vtysh_cmd(
+ "configure terminal\nrouter bgp 65500\naddress-family ipv6 unicast\nneighbor 192:2::100 activate\n",
+ isjson=False,
+ )
+
+ # Check r2 gets prefix 172:31::11/128
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_found,
+ tgen.gears["r2"],
+ "ipv6",
+ "172:31::11/128",
+ "444:1",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert (
+ success
+ ), "r2, prefix 172:31::11/128 from r11 not present. r11 still disconnected from rr ?"
+ bgp_vpnv6_table_check_all(tgen.gears["r2"])
+
+
+def test_recursive_route():
+ """
+ Test static recursive route redistributed over BGP
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Enabling recursive static route")
+ tgen.gears["r1"].vtysh_cmd(
+ "configure terminal\nvrf vrf1\nipv6 route 172:31::30/128 172:31::20\n",
+ isjson=False,
+ )
+ logger.info("Checking BGP VPNv6 labels on r2")
+ # that route should be sent along with label for 192.0.2.11
+
+ def _prefix30_not_found(router):
+ output = json.loads(router.vtysh_cmd("show bgp ipv6 vpn 172:31::30/128 json"))
+ expected = {"444:1": {"prefix": "172:31::30/128"}}
+ ret = topotest.json_cmp(output, expected)
+ if ret is None:
+ return "not good"
+ return None
+
+ def _prefix30_found(router):
+ output = json.loads(router.vtysh_cmd("show bgp ipv6 vpn 172:31::30/128 json"))
+ expected = {"444:1": {"prefix": "172:31::30/128"}}
+ return topotest.json_cmp(output, expected)
+
+ # Check r2 received vpnv6 update with 172:31::30
+ test_func = functools.partial(_prefix30_found, tgen.gears["r2"])
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r2, VPNv6 update 172:31::30 not found"
+
+ # that route should be sent along with label for 192::2:11
+ bgp_vpnv6_table_check(
+ tgen.gears["r2"],
+ group=PREFIXES_R11 + ["172:31::30/128"],
+ )
+
+ # diagnostic
+ logger.info("Dumping label nexthop table")
+ tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False)
+ logger.info("Dumping nexthop table")
+ tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 nexthop detail", isjson=False)
+
+ logger.info("Disabling recursive static route")
+ tgen.gears["r1"].vtysh_cmd(
+ "configure terminal\nvrf vrf1\nno ipv6 route 172:31::30/128 172:31::20\n",
+ isjson=False,
+ )
+
+ # Check r2 removed 172:31::30 vpnv6 update
+ test_func = functools.partial(_prefix30_not_found, tgen.gears["r2"])
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r2, VPNv6 update 172:31::30 still present"
+
+
+def test_prefix_changes_interface():
+ """
+ Test BGP update for a given prefix learnt on different interface
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Enabling a 172:31::50/128 prefix for r11")
+ tgen.gears["r11"].vtysh_cmd(
+ "configure terminal\nrouter bgp\naddress-family ipv6 unicast\nnetwork 172:31::50/128",
+ isjson=False,
+ )
+
+ # Check r2 received vpnv6 update with 172:31::50
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_found,
+ tgen.gears["r2"],
+ "ipv6",
+ "172:31::50/128",
+ "444:1",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r2, VPNv6 update 172:31::50 not found"
+
+ # diagnostic
+ logger.info("Dumping label nexthop table")
+ tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False)
+
+ label_list = set()
+ bgp_vpnv6_table_check(
+ tgen.gears["r2"],
+ group=PREFIXES_R11 + ["172:31::50/128"],
+ label_list=label_list,
+ )
+
+ assert (
+ len(label_list) == 1
+ ), "Multiple Label values found for updates from r11 found"
+
+ oldlabel = label_list.pop()
+ logger.info("r1, getting the outgoing interface used by label {}".format(oldlabel))
+ old_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], oldlabel)
+ logger.info(
+ "r1, outgoing interface used by label {} is {}".format(
+ oldlabel, old_outgoing_interface
+ )
+ )
+
+ logger.info("Moving the 172:31::50/128 prefix from r11 to r13")
+ tgen.gears["r11"].vtysh_cmd(
+ "configure terminal\nrouter bgp\naddress-family ipv6 unicast\nno network 172:31::50/128",
+ isjson=False,
+ )
+ tgen.gears["r13"].vtysh_cmd(
+ "configure terminal\nrouter bgp\naddress-family ipv6 unicast\nnetwork 172:31::50/128",
+ isjson=False,
+ )
+
+ # Check r2 removed 172:31::50 vpnv6 update with old label
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_not_found,
+ tgen.gears["r2"],
+ "ipv6",
+ "172:31::50/128",
+ "444:1",
+ label=oldlabel,
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert (
+ success
+ ), "r2, vpnv6 update 172:31::50 with old label {0} still present".format(oldlabel)
+
+ # diagnostic
+ logger.info("Dumping label nexthop table")
+ tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False)
+
+ # Check r2 received new 172:31::50 vpnv6 update
+ test_func = functools.partial(
+ check_show_bgp_vpn_prefix_found,
+ tgen.gears["r2"],
+ "ipv6",
+ "172:31::50/128",
+ "444:1",
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r2, vpnv6 update 172:31::50 not found"
+
+ label_list = set()
+ bgp_vpnv6_table_check(
+ tgen.gears["r2"],
+ group=["172:31::13/128", "172:31::50/128"],
+ label_list=label_list,
+ )
+ assert (
+ len(label_list) == 1
+ ), "Multiple Label values found for updates from r13 found"
+
+ newlabel = label_list.pop()
+ logger.info("r1, getting the outgoing interface used by label {}".format(newlabel))
+ new_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], newlabel)
+ logger.info(
+ "r1, outgoing interface used by label {} is {}".format(
+ newlabel, new_outgoing_interface
+ )
+ )
+ if old_outgoing_interface == new_outgoing_interface:
+ assert 0, "r1, outgoing interface did not change whereas BGP update moved"
+
+ logger.info("Restoring state by removing the 172:31::50/128 prefix from r13")
+ tgen.gears["r13"].vtysh_cmd(
+ "configure terminal\nrouter bgp\naddress-family ipv6 unicast\nno network 172:31::50/128",
+ isjson=False,
+ )
+
+
+def test_changing_default_label_value():
+ """
+ Change the MPLS default value
+ Check that r1 VPNv6 entries have the 222 label value
+ Check that MPLS entry with old label value is no more present
+ Check that MPLS entry for local traffic has inLabel set to 222
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+
+ # counting the number of labels used in the VPNv6 table
+ label_list = set()
+ logger.info("r1, VPNv6 table, check the number of labels used before modification")
+ bgp_vpnv6_table_check_all(router, label_list)
+ old_len = len(label_list)
+ assert (
+ old_len != 1
+ ), "r1, number of labels used should be greater than 1, oberved {} ".format(old_len)
+
+ logger.info("r1, vrf1, changing the default MPLS label value to export to 222")
+ router.vtysh_cmd(
+ "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nlabel vpn export 222\n",
+ isjson=False,
+ )
+
+ # Check r1 updated the MPLS entry with the 222 label value
+ logger.info(
+ "r1, mpls table, check that MPLS entry with inLabel set to 222 has vrf1 interface"
+ )
+ test_func = functools.partial(
+ check_show_mpls_table_entry_label_found, router, 222, "vrf1"
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r1, mpls entry with label 222 not found"
+
+ # check label repartition is ok
+ logger.info("r1, VPNv6 table, check the number of labels used after modification")
+ label_list = set()
+ bgp_vpnv6_table_check_all(router, label_list)
+ new_len = len(label_list)
+ assert (
+ old_len == new_len
+ ), "r1, number of labels after modification differ from previous, observed {}, expected {} ".format(
+ new_len, old_len
+ )
+
+ logger.info(
+ "r1, VPNv6 table, check that prefixes that were using the vrf label have refreshed the label value to 222"
+ )
+ bgp_vpnv6_table_check(router, group=PREFIXES_CONNECTED, label_value_expected=222)
+
+
+def test_unconfigure_allocation_mode_nexthop():
+ """
+ Test unconfiguring allocation mode per nexthop
+ Check on r2 that new MPLS label values have been propagated
+ Check that show mpls table has no entry with label 17 (previously used)
+ Check that all VPN updates on r1 should have label value moved to 222
+ Check that show mpls table will only have 222 label value
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Unconfiguring allocation mode per nexthop")
+ router = tgen.gears["r1"]
+ dump = router.vtysh_cmd(
+ "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nno label vpn export allocation-mode per-nexthop\n",
+ isjson=False,
+ )
+
+ # Check r1 updated the MPLS entry with the 222 label value
+ logger.info(
+ "r1, mpls table, check that MPLS entry with inLabel set to 17 is not present"
+ )
+ test_func = functools.partial(
+ check_show_mpls_table_entry_label_not_found, router, 17
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r1, mpls entry with label 17 still present"
+
+ # Check vpnv6 routes from r1
+ logger.info("Checking VPNv6 routes on r1")
+ label_list = set()
+ bgp_vpnv6_table_check_all(router, label_list=label_list, same=True)
+ assert len(label_list) == 1, "r1, multiple Label values found for VPNv6 updates"
+
+ new_label = label_list.pop()
+ assert (
+ new_label == 222
+ ), "r1, wrong label value in VPNv6 table, expected 222, observed {}".format(
+ new_label
+ )
+
+ # Check mpls table with 222 value
+ logger.info("Checking MPLS values on show mpls table of r1")
+ label_list = set()
+ label_list.add(222)
+ mpls_table_check(router, label_list=label_list)
+
+
+def test_reconfigure_allocation_mode_nexthop():
+ """
+ Test re-configuring allocation mode per nexthop
+ Check that show mpls table has no entry with label 17
+ Check that all VPN updates on r1 should have multiple label values and not only 222
+ Check that show mpls table will have multiple label values and not only 222
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Reconfiguring allocation mode per nexthop")
+ router = tgen.gears["r1"]
+ dump = router.vtysh_cmd(
+ "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nlabel vpn export allocation-mode per-nexthop\n",
+ isjson=False,
+ )
+
+ # Check that show mpls table has no entry with label 17
+ logger.info(
+ "r1, mpls table, check that MPLS entry with inLabel set to 17 is present"
+ )
+ test_func = functools.partial(
+ check_show_mpls_table_entry_label_not_found, router, 17
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert success, "r1, mpls entry with label 17 still present"
+
+ # Check vpnv6 routes from r1
+ logger.info("Checking VPNv6 routes on r1")
+ label_list = set()
+ bgp_vpnv6_table_check_all(router, label_list=label_list)
+ assert len(label_list) != 1, "r1, only 1 label values found for VPNv6 updates"
+
+ # Check mpls table with all values
+ logger.info("Checking MPLS values on show mpls table of r1")
+ mpls_table_check(router, label_list=label_list)
+
+
+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..c34c3eb
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py
@@ -0,0 +1,1903 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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..32643c2
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py
@@ -0,0 +1,890 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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]
+
+# 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 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_vrf_dynamic_route_leak_topo2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ 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_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_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_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_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..1787021
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo3/test_bgp_vrf_dynamic_route_leak_topo3.py
@@ -0,0 +1,1790 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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-1.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-1.py
new file mode 100644
index 0000000..c7fbc01
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-1.py
@@ -0,0 +1,394 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py
new file mode 100644
index 0000000..02950eb
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py
@@ -0,0 +1,919 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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_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)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py
new file mode 100644
index 0000000..4b18903
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py
@@ -0,0 +1,919 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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_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..244db6c
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/test_bgp_vrf_leaking.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2022, LINE Corporation
+# Authored by Ryoga Saito <ryoga.saito@linecorp.com>
+#
+
+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..42cbf1e
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/test_bgp_vrf_leaking_rt_change_route_maps.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+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..f352196
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo1.py
@@ -0,0 +1,879 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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_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_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_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..5d93964
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo2.py
@@ -0,0 +1,526 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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..32239e9
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_ipv6_rtadv.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by 6WIND
+#
+
+"""
+ 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_md5_peering/__init__.py b/tests/topotests/bgp_vrf_md5_peering/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_vrf_md5_peering/__init__.py
diff --git a/tests/topotests/bgp_vrf_md5_peering/exabgp.env b/tests/topotests/bgp_vrf_md5_peering/exabgp.env
new file mode 100644
index 0000000..28e6423
--- /dev/null
+++ b/tests/topotests/bgp_vrf_md5_peering/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_vrf_md5_peering/peer1/exabgp.cfg b/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg
new file mode 100644
index 0000000..3260513
--- /dev/null
+++ b/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg
@@ -0,0 +1,13 @@
+neighbor 10.0.0.1 {
+ router-id 10.0.0.2;
+ local-address 10.0.0.2;
+ local-as 65001;
+ peer-as 65534;
+ md5 test123;
+
+ static {
+ route 192.168.100.1/32 {
+ next-hop 10.0.0.2;
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf b/tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf
new file mode 100644
index 0000000..9f2ee19
--- /dev/null
+++ b/tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf
@@ -0,0 +1,11 @@
+!
+!debug bgp neighbor
+!
+router bgp 65534 vrf public
+ bgp router-id 10.0.0.1
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.2 remote-as external
+ neighbor 10.0.0.2 timers 3 10
+ neighbor 10.0.0.2 timers connect 1
+ neighbor 10.0.0.2 password test123
+!
diff --git a/tests/topotests/bgp_vrf_md5_peering/r1/zebra.conf b/tests/topotests/bgp_vrf_md5_peering/r1/zebra.conf
new file mode 100644
index 0000000..0c183ae
--- /dev/null
+++ b/tests/topotests/bgp_vrf_md5_peering/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0 vrf public
+ ip address 10.0.0.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_vrf_md5_peering/test_bgp_vrf_md5_peering.py b/tests/topotests/bgp_vrf_md5_peering/test_bgp_vrf_md5_peering.py
new file mode 100644
index 0000000..eefe586
--- /dev/null
+++ b/tests/topotests/bgp_vrf_md5_peering/test_bgp_vrf_md5_peering.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+Test if BGP MD5 basic authentication works per-VRF.
+"""
+
+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()
+
+ r1 = tgen.gears["r1"]
+ r1.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf"))
+ r1.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf"))
+ r1.start()
+
+ peer = tgen.gears["peer1"]
+ peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env"))
+
+ # VRF 'public'
+ r1.cmd_raises("ip link add public type vrf table 1001")
+ r1.cmd_raises("ip link set up dev public")
+ r1.cmd_raises("ip link set r1-eth0 master public")
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_vrf_md5_peering():
+ 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 vrf public 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)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+
+ assert result is None, "Can't peer with md5 per-VRF"
+
+
+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..8457b75
--- /dev/null
+++ b/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py
@@ -0,0 +1,212 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_vrf_netns_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018 by 6WIND
+#
+
+"""
+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..fd7ffff
--- /dev/null
+++ b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp-vrf-route-leak-basic.py
+#
+# Copyright (c) 2018 Cumulus Networks, Inc.
+# Donald Sharp
+#
+
+"""
+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..b4dc338
--- /dev/null
+++ b/tests/topotests/config_timing/r1/zebra.conf
@@ -0,0 +1,16 @@
+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
+
+route-map RM-NONE6 deny 10
+
+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..5c1b972
--- /dev/null
+++ b/tests/topotests/config_timing/test_config_timing.py
@@ -0,0 +1,262 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# 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>
+#
+
+"""
+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
+from lib import topotest
+
+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.debug(
+ "\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
+ router = tgen.gears["r1"]
+ output = router.net.cmd_legacy("vtysh -h | grep address-sanitizer", warn=False)
+ if output == "":
+ logger.info("No Address Sanitizer, generating 10000 routes")
+ prefix_count = 10000
+ else:
+ logger.info("Address Sanitizer build, only testing 50 routes")
+ prefix_count = 50
+
+ prefix_base = [
+ [u"10.0.0.0/8", u"11.0.0.0/8"],
+ [u"2100:1111:2220::/44", u"2100:3333:4440::/44"],
+ ]
+
+ # This apparently needed to allow for various mgmtd/staticd/zebra connections to form
+ # which then SLOWS execution down. If we don't include this value then the
+ # initial, baseline establishing, time is 2 time faster (e.g., 5s instead of 10s),
+ # but all later runs are slower and fail.
+ #
+ # This should be done differently based on actual facts.
+ topotest.sleep(5)
+
+ 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..0afebde
--- /dev/null
+++ b/tests/topotests/conftest.py
@@ -0,0 +1,691 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+"""
+Topotest conftest.py file.
+"""
+# pylint: disable=consider-using-f-string
+
+import contextlib
+import glob
+import logging
+import os
+import re
+import resource
+import subprocess
+import sys
+import time
+from pathlib import Path
+
+import lib.fixtures
+import pytest
+from lib.micronet_compat import Mininet
+from lib.topogen import diagnose_env, get_topogen
+from lib.topolog import get_test_logdir, logger
+from lib.topotest import json_cmp_result
+from munet import cli
+from munet.base import Commander, proc_error
+from munet.cleanup import cleanup_current, cleanup_previous
+from munet.config import ConfigOptionsProxy
+from munet.testing.util import pause_test
+
+from lib import topolog, topotest
+
+try:
+ # Used by munet native tests
+ from munet.testing.fixtures import event_loop, unet # pylint: disable=all # noqa
+
+ @pytest.fixture(scope="module")
+ def rundir_module(pytestconfig):
+ d = os.path.join(pytestconfig.option.rundir, get_test_logdir())
+ logging.debug("rundir_module: test module rundir %s", d)
+ return d
+
+except (AttributeError, ImportError):
+ pass
+
+
+# Remove this and use munet version when we move to pytest_asyncio
+@contextlib.contextmanager
+def chdir(ndir, desc=""):
+ odir = os.getcwd()
+ os.chdir(ndir)
+ if desc:
+ logging.debug("%s: chdir from %s to %s", desc, odir, ndir)
+ try:
+ yield
+ finally:
+ if desc:
+ logging.debug("%s: chdir back from %s to %s", desc, ndir, odir)
+ os.chdir(odir)
+
+
+@contextlib.contextmanager
+def log_handler(basename, logpath):
+ topolog.logstart(basename, logpath)
+ try:
+ yield
+ finally:
+ topolog.logfinish(basename, logpath)
+
+
+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(
+ "--logd",
+ action="append",
+ metavar="DAEMON[,ROUTER[,...]",
+ help=(
+ "Tail-F the DAEMON log file on all or a subset of ROUTERs."
+ " Option can be given multiple times."
+ ),
+ )
+
+ parser.addoption(
+ "--memleaks",
+ action="store_true",
+ help="Report memstat results as errors",
+ )
+
+ parser.addoption(
+ "--pause",
+ action="store_true",
+ help="Pause after each test",
+ )
+
+ parser.addoption(
+ "--pause-at-end",
+ action="store_true",
+ help="Pause before taking munet down",
+ )
+
+ 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)",
+ )
+
+ parser.addoption(
+ "--pcap",
+ default="",
+ metavar="NET[,NET...]",
+ help="Comma-separated list of networks to capture packets on, or 'all'",
+ )
+
+ parser.addoption(
+ "--perf",
+ action="append",
+ metavar="DAEMON[,ROUTER[,...]",
+ help=(
+ "Collect performance data from given DAEMON on all or a subset of ROUTERs."
+ " Option can be given multiple times."
+ ),
+ )
+
+ parser.addoption(
+ "--perf-options",
+ metavar="OPTS",
+ default="-g",
+ help="Options to pass to `perf record`.",
+ )
+
+ 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_valgrind_memleaks():
+ assert topotest.g_pytest_config.option.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))
+
+
+def check_for_memleaks():
+ leaks = []
+ tgen = get_topogen() # pylint: disable=redefined-outer-name
+ latest = []
+ existing = []
+ if tgen is not None:
+ logdir = tgen.logdir
+ if hasattr(tgen, "memstat_existing_files"):
+ existing = tgen.memstat_existing_files
+ latest = glob.glob(os.path.join(logdir, "*/*.err"))
+
+ daemons = []
+ for vfile in latest:
+ if vfile in existing:
+ continue
+ with open(vfile, encoding="ascii") as vf:
+ vfcontent = vf.read()
+ num = vfcontent.count("memstats:")
+ if num:
+ existing.append(vfile) # have summary don't check again
+ emsg = "{} types in {}".format(num, vfile)
+ leaks.append(emsg)
+ daemon = re.match(r".*test[a-z_A-Z0-9\+]*/(.*)\.err", vfile).group(1)
+ daemons.append("{}({})".format(daemon, num))
+
+ if tgen is not None:
+ tgen.memstat_existing_files = existing
+
+ if leaks:
+ logger.error("memleaks found:\n\t%s", "\n\t".join(leaks))
+ pytest.fail("memleaks found for daemons: " + " ".join(daemons))
+
+
+def check_for_core_dumps():
+ tgen = get_topogen() # pylint: disable=redefined-outer-name
+ if not tgen:
+ return
+
+ if not hasattr(tgen, "existing_core_files"):
+ tgen.existing_core_files = set()
+ existing = tgen.existing_core_files
+
+ cores = glob.glob(os.path.join(tgen.logdir, "*/*.dmp"))
+ latest = {x for x in cores if x not in existing}
+ if latest:
+ existing |= latest
+ tgen.existing_core_files = existing
+
+ emsg = "New core[s] found: " + ", ".join(latest)
+ logger.error(emsg)
+ pytest.fail(emsg)
+
+
+def check_for_backtraces():
+ tgen = get_topogen() # pylint: disable=redefined-outer-name
+ if not tgen:
+ return
+
+ if not hasattr(tgen, "existing_backtrace_files"):
+ tgen.existing_backtrace_files = {}
+ existing = tgen.existing_backtrace_files
+
+ latest = glob.glob(os.path.join(tgen.logdir, "*/*.log"))
+ backtraces = []
+ for vfile in latest:
+ with open(vfile, encoding="ascii") as vf:
+ vfcontent = vf.read()
+ btcount = vfcontent.count("Backtrace:")
+ if not btcount:
+ continue
+ if vfile not in existing:
+ existing[vfile] = 0
+ if btcount == existing[vfile]:
+ continue
+ existing[vfile] = btcount
+ backtraces.append(vfile)
+
+ if backtraces:
+ emsg = "New backtraces found in: " + ", ".join(backtraces)
+ logger.error(emsg)
+ pytest.fail(emsg)
+
+
+@pytest.fixture(autouse=True, scope="module")
+def module_autouse(request):
+ basename = get_test_logdir(request.node.nodeid, True)
+ logdir = Path(topotest.g_pytest_config.option.rundir) / basename
+ logpath = logdir / "exec.log"
+
+ subprocess.check_call("mkdir -p -m 1777 {}".format(logdir), shell=True)
+
+ with log_handler(basename, logpath):
+ sdir = os.path.dirname(os.path.realpath(request.fspath))
+ with chdir(sdir, "module autouse fixture"):
+ yield
+
+
+@pytest.fixture(autouse=True, scope="module")
+def module_check_memtest(request):
+ yield
+ if request.config.option.valgrind_memleaks:
+ if get_topogen() is not None:
+ check_for_valgrind_memleaks()
+ if request.config.option.memleaks:
+ if get_topogen() is not None:
+ check_for_memleaks()
+
+
+#
+# Disable per test function logging as FRR CI system can't handle it.
+#
+# @pytest.fixture(autouse=True, scope="function")
+# def function_autouse(request):
+# # For tests we actually use the logdir name as the logfile base
+# logbase = get_test_logdir(nodeid=request.node.nodeid, module=False)
+# logbase = os.path.join(topotest.g_pytest_config.option.rundir, logbase)
+# logpath = Path(logbase)
+# path = Path(f"{logpath.parent}/exec-{logpath.name}.log")
+# subprocess.check_call("mkdir -p -m 1777 {}".format(logpath.parent), shell=True)
+# with log_handler(request.node.nodeid, path):
+# yield
+
+
+@pytest.hookimpl(hookwrapper=True)
+def pytest_runtest_call(item: pytest.Item) -> None:
+ "Hook the function that is called to execute the test."
+
+ # For topology only run the CLI then exit
+ if item.config.option.topology_only:
+ get_topogen().cli()
+ pytest.exit("exiting after --topology-only")
+
+ # Let the default pytest_runtest_call execute the test function
+ yield
+
+ check_for_backtraces()
+ check_for_core_dumps()
+
+ # Check for leaks if requested
+ if item.config.option.valgrind_memleaks:
+ check_for_valgrind_memleaks()
+
+ if item.config.option.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.
+ """
+ topotest.g_pytest_config = ConfigOptionsProxy(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
+ wname = ""
+ else:
+ wname = os.environ["PYTEST_XDIST_WORKER"]
+ os.environ["PYTEST_TOPOTEST_WORKER"] = wname
+ 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.option.rundir
+ if not rundir:
+ rundir = config.getini("rundir")
+ if not rundir:
+ rundir = "/tmp/topotests"
+ config.option.rundir = rundir
+
+ 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])
+
+ # 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")
+
+ # Handle pytest-xdist each worker get's it's own top level log file
+ # `exec-worker-N.log`
+ if wname:
+ wname = wname.replace("gw", "worker-")
+ cpath = Path(config.option.log_file).absolute()
+ config.option.log_file = f"{cpath.parent}/{cpath.stem}-{wname}{cpath.suffix}"
+ elif is_xdist:
+ cpath = Path(config.option.log_file).absolute()
+ config.option.log_file = f"{cpath.parent}/{cpath.stem}-xdist{cpath.suffix}"
+
+ # 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))
+
+ #
+ # Check for window capability if given options that require window
+ #
+ assert_feature_windows(config.option.gdb_routers, "GDB")
+ assert_feature_windows(config.option.gdb_daemons, "GDB")
+ assert_feature_windows(config.option.cli_on_error, "--cli-on-error")
+ assert_feature_windows(config.option.shell, "--shell")
+ assert_feature_windows(config.option.shell_on_error, "--shell-on-error")
+ assert_feature_windows(config.option.vtysh, "--vtysh")
+ assert_feature_windows(config.option.vtysh_on_error, "--vtysh-on-error")
+
+ if config.option.topology_only and is_xdist:
+ pytest.exit("Cannot use --topology-only with distributed test mode")
+
+ pytest.exit("Cannot use --topology-only with distributed test mode")
+
+ # Check environment now that we have config
+ if not diagnose_env(rundir):
+ pytest.exit("environment has errors, please read the logs in %s" % rundir)
+
+ # slave TOPOTESTS_CHECK_MEMLEAK to memleaks flag
+ if config.option.memleaks:
+ if "TOPOTESTS_CHECK_MEMLEAK" not in os.environ:
+ os.environ["TOPOTESTS_CHECK_MEMLEAK"] = "/dev/null"
+ else:
+ if "TOPOTESTS_CHECK_MEMLEAK" in os.environ:
+ del os.environ["TOPOTESTS_CHECK_MEMLEAK"]
+ if "TOPOTESTS_CHECK_STDERR" in os.environ:
+ del os.environ["TOPOTESTS_CHECK_STDERR"]
+
+
+@pytest.fixture(autouse=True, scope="session")
+def setup_session_auto():
+ # Aligns logs nicely
+ logging.addLevelName(logging.WARNING, " WARN")
+ logging.addLevelName(logging.INFO, " INFO")
+
+ 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"
+
+ pause = bool(item.config.getoption("--pause"))
+ 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 = item.config.option.pause_on_error or item.config.option.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 item.config.option.vtysh_on_error:
+ error_cmd = commander.get_exec_path(["vtysh"])
+ elif error and item.config.option.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 item.config.option.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.cli(Mininet.g_mnet_inst, title=title, background=False)
+ else:
+ logger.error("Could not launch CLI b/c no mininet exists yet")
+
+ if pause and isatty:
+ pause_test()
+
+
+#
+# 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..2dc7329
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r1/isisd.conf
@@ -0,0 +1,35 @@
+!
+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-interval 1
+ isis hello-multiplier 10
+!
+interface r1-eth1
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..465034f
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r1/sharpd.conf
@@ -0,0 +1,2 @@
+!
+!
diff --git a/tests/topotests/cspf_topo1/r1/zebra.conf b/tests/topotests/cspf_topo1/r1/zebra.conf
new file mode 100644
index 0000000..3b615b0
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r1/zebra.conf
@@ -0,0 +1,28 @@
+!
+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
+ max-bw 10e+10
+ 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..59bb3be
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r2/isisd.conf
@@ -0,0 +1,50 @@
+!
+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-interval 1
+ isis hello-multiplier 10
+!
+interface r2-eth1
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface r2-eth2
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface r2-eth3
+ ip router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..55d9563
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r2/zebra.conf
@@ -0,0 +1,46 @@
+!
+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
+ max-bw 10e+10
+ 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..7d3e4de
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r3/isisd.conf
@@ -0,0 +1,36 @@
+!
+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-interval 1
+ isis hello-multiplier 10
+!
+interface r3-eth1
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+!
+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..674b738
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r4/isisd.conf
@@ -0,0 +1,42 @@
+!
+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-interval 1
+ isis hello-multiplier 10
+!
+interface r4-eth1
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+!
+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..359b655
--- /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":"2001:db8:1::1: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":10,
+ "local-address-v6":"2001:db8:1::1:1",
+ "remote-address-v6":"2001:db8:1::1:2",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":20000
+ }
+ },
+ {
+ "edge-id":"2001:db8:1::1:2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":20000
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":40000
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000
+ }
+ },
+ {
+ "edge-id":"2001:db8:5::3:4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000
+ },
+ "segment-routing":[
+ {
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"2001:db8:5::4:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.0.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000
+ }
+ },
+ {
+ "edge-id":"10.0.1.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":10,
+ "local-address":"10.0.1.1",
+ "remote-address":"10.0.1.2",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":20000
+ }
+ },
+ {
+ "edge-id":"10.0.1.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":20000
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":40000
+ }
+ },
+ {
+ "edge-id":"10.0.3.3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000
+ }
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.4.4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "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..072a2dd
--- /dev/null
+++ b/tests/topotests/cspf_topo1/test_cspf_topo1.py
@@ -0,0 +1,304 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_cspf_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2022 by Orange
+# Author: Olivier Dugeon <olivier.dugeon@orange.com>
+#
+
+"""
+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=5, 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 1000000",
+ )
+
+
+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 1000000",
+ )
+
+
+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 10000000",
+ )
+ 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..aec2058
--- /dev/null
+++ b/tests/topotests/docker/build.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+# SPDX-License-Identifier: MIT
+#
+# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF")
+
+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..ce373d9
--- /dev/null
+++ b/tests/topotests/docker/frr-topotests.sh
@@ -0,0 +1,155 @@
+#!/bin/bash
+# SPDX-License-Identifier: MIT
+#
+# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF")
+
+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..3b296a1
--- /dev/null
+++ b/tests/topotests/docker/inner/compile_frr.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+# SPDX-License-Identifier: MIT
+#
+# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF")
+
+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..44e16db
--- /dev/null
+++ b/tests/topotests/docker/inner/entrypoint.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+# SPDX-License-Identifier: MIT
+#
+# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF")
+
+# 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..4ebacbb
--- /dev/null
+++ b/tests/topotests/docker/inner/funcs.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+# SPDX-License-Identifier: MIT
+#
+# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF")
+
+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..926a25c
--- /dev/null
+++ b/tests/topotests/docker/inner/openvswitch.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+# SPDX-License-Identifier: MIT
+#
+# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF")
+
+# 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..3c9392c
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/test_eigrp_topo1.py
@@ -0,0 +1,279 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_eigrp_topo1.py
+#
+# Copyright (c) 2017 by
+# Cumulus Networks, Inc.
+# Donald Sharp
+#
+
+"""
+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..7f37ced
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/spine/bgp.summ.json
@@ -0,0 +1,39 @@
+{
+ "routerId":"192.168.100.1",
+ "as":65001,
+ "vrfName":"default",
+ "tableVersion":7,
+ "peerCount":2,
+ "peers":{
+ "192.168.1.2":{
+ "remoteAs":65002,
+ "version":4,
+ "outq":0,
+ "inq":0,
+ "pfxRcd":3,
+ "pfxSnt":7,
+ "state":"Established",
+ "connectionsEstablished":1,
+ "connectionsDropped":0,
+ "idType":"ipv4"
+ },
+ "192.168.2.3":{
+ "remoteAs":65003,
+ "version":4,
+ "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..c0621d7
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/test_evpn_pim_topo1.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_evpn-pim_topo1.py
+#
+# Copyright (c) 2017 by
+# Cumulus Networks, Inc.
+# Donald Sharp
+#
+
+"""
+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..4586866
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py
@@ -0,0 +1,1007 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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 "
+ "Expected: Routes should not be present in {} RIB \n "
+ "Found: {}".format(tc_name, "d2", 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 "
+ "Expected: Malformed Auto-RT value should not be accepted in {} \n "
+ "Found: {}".format(tc_name, "d2", 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 "
+ "Expected: Malformed Auto-RT value should not be accepted in {} \n "
+ "Found: {}".format(tc_name, "d2", 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..10d216a
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
@@ -0,0 +1,2322 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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]
+
+# 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 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 = "{}/evpn_type5_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ 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_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: Routes should not be present in {} RIB \n "
+ "Found: {}".format(tc_name, "d1", result)
+ )
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Expected: Routes should not be present in {} RIB \n "
+ "Found: {}".format(tc_name, "d2", 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: RT value out of boundary error in {} \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} RIB \n "
+ "Found: {}".format(tc_name, "d2", 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 "
+ "Expected: Routes should not be present in {} RIB \n "
+ "Found: {}".format(tc_name, "r3", 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 "
+ "Expected: Routes should not be present in {} RIB \n "
+ "Found: {}".format(tc_name, "d2", result)
+ )
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Expected: Routes should not be present in {} RIB \n "
+ "Found: {}".format(tc_name, "r4", 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 "
+ "Expected: Routes should not be present in {} RIB \n "
+ "Found: {}".format(tc_name, "d2", result)
+ )
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Expected: Routes should not be present in {} RIB \n "
+ "Found: {}".format(tc_name, "r4", 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_munet/munet.yaml b/tests/topotests/example_munet/munet.yaml
new file mode 100644
index 0000000..34e1470
--- /dev/null
+++ b/tests/topotests/example_munet/munet.yaml
@@ -0,0 +1,17 @@
+version: 1
+topology:
+ ipv6-enable: true
+ networks-autonumber: true
+ networks:
+ - name: net1
+ - name: net2
+ nodes:
+ - name: r1
+ kind: frr
+ connections: ["net1"]
+ - name: r2
+ kind: frr
+ connections: ["net1", "net2"]
+ - name: r3
+ kind: frr
+ connections: ["net2"]
diff --git a/tests/topotests/example_munet/r1/daemons b/tests/topotests/example_munet/r1/daemons
new file mode 100644
index 0000000..a454c95
--- /dev/null
+++ b/tests/topotests/example_munet/r1/daemons
@@ -0,0 +1,6 @@
+zebra=1
+staticd=1
+vtysh_enable=1
+watchfrr_enable=1
+zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log"
+staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log"
diff --git a/tests/topotests/example_munet/r1/frr.conf b/tests/topotests/example_munet/r1/frr.conf
new file mode 100644
index 0000000..468bda5
--- /dev/null
+++ b/tests/topotests/example_munet/r1/frr.conf
@@ -0,0 +1,7 @@
+log file /var/log/frr/frr.log
+service integrated-vtysh-config
+
+interface eth0
+ ip address 10.0.1.1/24
+
+ip route 10.0.0.0/8 blackhole
diff --git a/tests/topotests/example_munet/r1/vtysh.conf b/tests/topotests/example_munet/r1/vtysh.conf
new file mode 100644
index 0000000..f863f56
--- /dev/null
+++ b/tests/topotests/example_munet/r1/vtysh.conf
@@ -0,0 +1 @@
+service integrated-vtysh-config \ No newline at end of file
diff --git a/tests/topotests/example_munet/r2/daemons b/tests/topotests/example_munet/r2/daemons
new file mode 100644
index 0000000..a454c95
--- /dev/null
+++ b/tests/topotests/example_munet/r2/daemons
@@ -0,0 +1,6 @@
+zebra=1
+staticd=1
+vtysh_enable=1
+watchfrr_enable=1
+zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log"
+staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log"
diff --git a/tests/topotests/example_munet/r2/frr.conf b/tests/topotests/example_munet/r2/frr.conf
new file mode 100644
index 0000000..77d9892
--- /dev/null
+++ b/tests/topotests/example_munet/r2/frr.conf
@@ -0,0 +1,10 @@
+log file /var/log/frr/frr.log
+service integrated-vtysh-config
+
+interface eth0
+ ip address 10.0.1.2/24
+
+interface eth1
+ ip address 10.0.2.2/24
+
+ip route 10.0.0.0/8 blackhole
diff --git a/tests/topotests/example_munet/r2/vtysh.conf b/tests/topotests/example_munet/r2/vtysh.conf
new file mode 100644
index 0000000..f863f56
--- /dev/null
+++ b/tests/topotests/example_munet/r2/vtysh.conf
@@ -0,0 +1 @@
+service integrated-vtysh-config \ No newline at end of file
diff --git a/tests/topotests/example_munet/r3/daemons b/tests/topotests/example_munet/r3/daemons
new file mode 100644
index 0000000..a454c95
--- /dev/null
+++ b/tests/topotests/example_munet/r3/daemons
@@ -0,0 +1,6 @@
+zebra=1
+staticd=1
+vtysh_enable=1
+watchfrr_enable=1
+zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log"
+staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log"
diff --git a/tests/topotests/example_munet/r3/frr.conf b/tests/topotests/example_munet/r3/frr.conf
new file mode 100644
index 0000000..e0839e6
--- /dev/null
+++ b/tests/topotests/example_munet/r3/frr.conf
@@ -0,0 +1,7 @@
+log file /var/log/frr/frr.log
+service integrated-vtysh-config
+
+interface eth0
+ ip address 10.0.2.3/24
+
+ip route 10.0.0.0/8 blackhole
diff --git a/tests/topotests/example_munet/r3/vtysh.conf b/tests/topotests/example_munet/r3/vtysh.conf
new file mode 100644
index 0000000..f863f56
--- /dev/null
+++ b/tests/topotests/example_munet/r3/vtysh.conf
@@ -0,0 +1 @@
+service integrated-vtysh-config \ No newline at end of file
diff --git a/tests/topotests/example_munet/test_munet.py b/tests/topotests/example_munet/test_munet.py
new file mode 100644
index 0000000..0d9599f
--- /dev/null
+++ b/tests/topotests/example_munet/test_munet.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# April 23 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+async def test_native_test(unet):
+ o = unet.hosts["r1"].cmd_nostatus("ip addr")
+ print(o)
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..2797548
--- /dev/null
+++ b/tests/topotests/example_test/test_template.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+#
+# <template>.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+<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..7a53260
--- /dev/null
+++ b/tests/topotests/example_test/test_template_json.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: ISC
+#
+# 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")
+#
+
+"""
+<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..49afc82
--- /dev/null
+++ b/tests/topotests/example_topojson_test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py
@@ -0,0 +1,184 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+
+"""
+<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..120399c
--- /dev/null
+++ b/tests/topotests/example_topojson_test/test_topo_json_single_link/test_example_topojson.py
@@ -0,0 +1,184 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+
+"""
+<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..4f3a8d8
--- /dev/null
+++ b/tests/topotests/example_topojson_test/test_topo_json_single_link_loopback/test_example_topojson.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+"""
+<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..88bfa98
--- /dev/null
+++ b/tests/topotests/grpc_basic/test_basic_grpc.py
@@ -0,0 +1,166 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# February 21 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+#
+"""
+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_advertise_high_metrics/__init__.py b/tests/topotests/isis_advertise_high_metrics/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_advertise_high_metrics/__init__.py
diff --git a/tests/topotests/isis_advertise_high_metrics/r1/isisd.conf b/tests/topotests/isis_advertise_high_metrics/r1/isisd.conf
new file mode 100644
index 0000000..747c64e
--- /dev/null
+++ b/tests/topotests/isis_advertise_high_metrics/r1/isisd.conf
@@ -0,0 +1,19 @@
+hostname r1
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+interface eth-r2
+ ip router isis 1
+ isis circuit-type level-2-only
+ isis network point-to-point
+!
+interface eth-r3
+ ip router isis 1
+ isis circuit-type level-2-only
+ isis metric 20
+ isis network point-to-point
+!
+router isis 1
+ is-type level-2-only
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0000.00
+! \ No newline at end of file
diff --git a/tests/topotests/isis_advertise_high_metrics/r1/zebra.conf b/tests/topotests/isis_advertise_high_metrics/r1/zebra.conf
new file mode 100644
index 0000000..b14ce0d
--- /dev/null
+++ b/tests/topotests/isis_advertise_high_metrics/r1/zebra.conf
@@ -0,0 +1,5 @@
+interface eth-r2
+ ip address 192.168.1.0/31
+
+interface eth-r3
+ ip address 192.168.1.2/31 \ No newline at end of file
diff --git a/tests/topotests/isis_advertise_high_metrics/r2/isisd.conf b/tests/topotests/isis_advertise_high_metrics/r2/isisd.conf
new file mode 100644
index 0000000..cee62ad
--- /dev/null
+++ b/tests/topotests/isis_advertise_high_metrics/r2/isisd.conf
@@ -0,0 +1,18 @@
+hostname r2
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+interface eth-r1
+ ip router isis 1
+ isis circuit-type level-2-only
+ isis network point-to-point
+!
+interface eth-r4
+ ip router isis 1
+ isis circuit-type level-2-only
+ isis network point-to-point
+!
+router isis 1
+ is-type level-2-only
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00
+! \ No newline at end of file
diff --git a/tests/topotests/isis_advertise_high_metrics/r2/zebra.conf b/tests/topotests/isis_advertise_high_metrics/r2/zebra.conf
new file mode 100644
index 0000000..01de593
--- /dev/null
+++ b/tests/topotests/isis_advertise_high_metrics/r2/zebra.conf
@@ -0,0 +1,5 @@
+interface eth-r1
+ ip address 192.168.1.1/31
+
+interface eth-r4
+ ip address 192.168.1.7/31 \ No newline at end of file
diff --git a/tests/topotests/isis_advertise_high_metrics/r3/isisd.conf b/tests/topotests/isis_advertise_high_metrics/r3/isisd.conf
new file mode 100644
index 0000000..6d795f0
--- /dev/null
+++ b/tests/topotests/isis_advertise_high_metrics/r3/isisd.conf
@@ -0,0 +1,20 @@
+hostname r3
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+interface eth-r1
+ ip router isis 1
+ isis circuit-type level-2-only
+ isis metric 20
+ isis network point-to-point
+!
+interface eth-r4
+ ip router isis 1
+ isis circuit-type level-2-only
+ isis metric 20
+ isis network point-to-point
+!
+router isis 1
+ is-type level-2-only
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00
+! \ No newline at end of file
diff --git a/tests/topotests/isis_advertise_high_metrics/r3/zebra.conf b/tests/topotests/isis_advertise_high_metrics/r3/zebra.conf
new file mode 100644
index 0000000..668431c
--- /dev/null
+++ b/tests/topotests/isis_advertise_high_metrics/r3/zebra.conf
@@ -0,0 +1,5 @@
+interface eth-r1
+ ip address 192.168.1.3/31
+
+interface eth-r4
+ ip address 192.168.1.4/31 \ No newline at end of file
diff --git a/tests/topotests/isis_advertise_high_metrics/r4/isisd.conf b/tests/topotests/isis_advertise_high_metrics/r4/isisd.conf
new file mode 100644
index 0000000..b281014
--- /dev/null
+++ b/tests/topotests/isis_advertise_high_metrics/r4/isisd.conf
@@ -0,0 +1,19 @@
+hostname r4
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+interface eth-r2
+ ip router isis 1
+ isis circuit-type level-2-only
+ isis network point-to-point
+!
+interface eth-r3
+ ip router isis 1
+ isis circuit-type level-2-only
+ isis metric 20
+ isis network point-to-point
+!
+router isis 1
+ is-type level-2-only
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0003.00
+! \ No newline at end of file
diff --git a/tests/topotests/isis_advertise_high_metrics/r4/zebra.conf b/tests/topotests/isis_advertise_high_metrics/r4/zebra.conf
new file mode 100644
index 0000000..6819dbb
--- /dev/null
+++ b/tests/topotests/isis_advertise_high_metrics/r4/zebra.conf
@@ -0,0 +1,5 @@
+interface eth-r2
+ ip address 192.168.1.6/31
+
+interface eth-r3
+ ip address 192.168.1.5/31
diff --git a/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py b/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py
new file mode 100644
index 0000000..ada8c0f
--- /dev/null
+++ b/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py
@@ -0,0 +1,473 @@
+#!/usr/bin/env python
+
+#
+# test_isis_advertise_high_metrics.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_isis_advertise_high_metrics.py: Advertise High Metrics FRR ISIS Test
+"""
+
+import os
+import re
+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.common_config import (
+ retry,
+ stop_router,
+ start_router,
+)
+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"
+
+ # Add ISIS routers:
+ # r2
+ # / \
+ # r1 r4
+ # \ /
+ # r3
+
+ #
+ # 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"], nodeif="eth-r2")
+ switch.add_link(tgen.gears["r2"], nodeif="eth-r1")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"], nodeif="eth-r3")
+ switch.add_link(tgen.gears["r3"], nodeif="eth-r1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"], nodeif="eth-r4")
+ switch.add_link(tgen.gears["r4"], nodeif="eth-r2")
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r3"], nodeif="eth-r4")
+ switch.add_link(tgen.gears["r4"], nodeif="eth-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_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()
+
+
+@retry(retry_timeout=60)
+def _check_interface_metrics(router, expected_metrics):
+ "Verfiy metrics on router's isis interfaces"
+
+ tgen = get_topogen()
+ router = tgen.gears[router]
+ logger.info(f"check_interface_metrics {router}")
+ isis_interface_output = router.vtysh_cmd("show isis interface detail json")
+
+ intf_json = json.loads(isis_interface_output)
+ for i in range(len(expected_metrics)):
+ metric = intf_json["areas"][0]["circuits"][i]["interface"]["levels"][0][
+ "metric"
+ ]
+ if metric != expected_metrics[i]:
+ intf_name = intf_json["areas"][0]["circuits"][i]["interface"]["name"]
+ return "{} with expected metric {} on {} got {}".format(
+ router.name, expected_metrics[i], intf_name, metric
+ )
+ return True
+
+
+def check_interface_metrics(router, expected_metrics):
+ "Verfiy metrics on router's isis interfaces"
+
+ assertmsg = _check_interface_metrics(router, expected_metrics)
+ assert assertmsg is True, assertmsg
+
+
+@retry(retry_timeout=60)
+def _check_lsp_metrics(router, lsp, expected_metrics):
+ "Verfiy metrics on router's lsp"
+ tgen = get_topogen()
+ router = tgen.gears[router]
+ logger.info(f"check_lsp_metrics {router}")
+ isis_lsp_output = router.vtysh_cmd("show isis database detail {}".format(lsp))
+
+ metrics_list = [int(i) for i in re.findall(r"Metric: (\d+)", isis_lsp_output)]
+ if len(metrics_list) == 0:
+ return False
+ for metric in metrics_list:
+ if metric not in expected_metrics:
+ return "{} with expected metrics {} got {}".format(
+ router.name, expected_metrics, metrics_list
+ )
+
+ return True
+
+
+def check_lsp_metrics(router, lsp, expected_metrics):
+ "Verfiy metrics on router's lsp"
+
+ assertmsg = _check_lsp_metrics(router, lsp, expected_metrics)
+ assert assertmsg is True, assertmsg
+
+
+@retry(retry_timeout=60)
+def _check_ip_route(router, destination, expected_interface):
+ "Verfiy IS-IS route"
+
+ tgen = get_topogen()
+ router = tgen.gears[router]
+ logger.info(f"check_ip_route {router}")
+ route_output = router.vtysh_cmd("show ip route {} json".format(destination))
+ route_json = json.loads(route_output)
+
+ interface = route_json[destination][0]["nexthops"][0]["interfaceName"]
+
+ if interface != expected_interface:
+ return "{} with expected route to {} got {} expected {}".format(
+ router.name, destination, interface, expected_interface
+ )
+
+ return True
+
+
+def check_ip_route(router, destination, expected_interface):
+ "Verfiy IS-IS route"
+
+ assertmsg = _check_ip_route(router, destination, expected_interface)
+ assert assertmsg is True, assertmsg
+
+
+def test_isis_daemon_up():
+ "Check isis daemon up before starting test"
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for router in ["r1", "r2", "r3", "r4"]:
+ r = tgen.gears[router]
+ daemons = r.vtysh_cmd("show daemons")
+ assert "isisd" in daemons
+
+ # Verify initial metric values.
+ check_lsp_metrics("r1", "r1.00-00", [10, 20])
+ check_lsp_metrics("r2", "r2.00-00", [10, 10])
+ check_lsp_metrics("r3", "r3.00-00", [20, 20])
+ check_lsp_metrics("r4", "r4.00-00", [10, 20])
+
+
+def test_isis_advertise_high_metrics():
+ "Check that advertise high metrics behaves as expected"
+
+ tgen = get_topogen()
+ net = get_topogen().net
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Testing advertise high metrics basic behavior")
+
+ # Confirm low metrics values on each isis interface on r1
+ r1 = tgen.gears["r1"]
+ check_interface_metrics("r1", [10, 20])
+
+ # Confirm low metrics values within isis database on r1
+ check_lsp_metrics("r1", "r1.00-00", [10, 20])
+
+ # Configure advertise high metrics
+ r1.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ advertise-high-metrics
+ """
+ )
+
+ # Confirm high wide metrics values on each isis interface on r1
+ check_interface_metrics("r1", [16777215])
+
+ # Confirm high wide metrics values within isis database on r1
+ check_lsp_metrics("r1", "r1.00-00", [16777215])
+
+ # Remove advertise high metrics
+ r1.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ no advertise-high-metrics
+ """
+ )
+
+ # Confirm low metrics values on each isis interface on r1
+ check_interface_metrics("r1", [10, 20])
+
+ # Confirm low metrics values within isis database on r1
+ check_lsp_metrics("r1", "r1.00-00", [10, 20])
+
+
+def test_isis_advertise_high_metrics_narrow():
+ "Check that advertise high metrics behaves as expected with narrow metrics"
+
+ tgen = get_topogen()
+ net = get_topogen().net
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Testing advertise high metrics with narrow metric style")
+
+ r1 = tgen.gears["r1"]
+
+ # Configure narrow metric-style
+ r1.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ metric-style narrow
+ """
+ )
+
+ # Confirm low metrics values on each isis interface on r1
+ check_interface_metrics("r1", [10, 20])
+
+ # Confirm low metrics values within isis database on r1
+ check_lsp_metrics("r1", "r1.00-00", [10, 20])
+
+ # Configure advertise high metrics
+ r1.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ advertise-high-metrics
+ """
+ )
+
+ # Confirm high narrow metrics values on each isis interface on r1
+ check_interface_metrics("r1", [63])
+
+ # Confirm high narrow metrics values within isis database on r1
+ check_lsp_metrics("r1", "r1.00-00", [63])
+
+ # Remove advertise high metrics
+ r1.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ no advertise-high-metrics
+ """
+ )
+
+ # Confirm low metrics values on each isis interface on r1
+ check_interface_metrics("r1", [10, 20])
+
+ # Confirm low metrics values within isis database on r1
+ check_lsp_metrics("r1", "r1.00-00", [10, 20])
+
+ # Remove narrow metric-style
+ r1.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ no metric-style narrow
+ """
+ )
+
+
+def test_isis_advertise_high_metrics_transition():
+ "Check that advertise high metrics behaves as expected with transition metrics"
+ tgen = get_topogen()
+ net = get_topogen().net
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Testing advertise high metrics with transition metric style")
+
+ r1 = tgen.gears["r1"]
+
+ # Configure transition metric-style
+ r1.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ metric-style transition
+ """
+ )
+
+ # Confirm low metrics values on each isis interface on r1
+ check_interface_metrics("r1", [10, 20])
+
+ # Confirm low metrics values within isis database on r1
+ check_lsp_metrics("r1", "r1.00-00", [10, 20])
+
+ # Configure advertise high metrics
+ r1.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ advertise-high-metrics
+ """
+ )
+
+ # Confirm high transition metrics values on each isis interface on r1
+ check_interface_metrics("r1", [62])
+
+ # Confirm high transition metrics values within isis database on r1
+ check_lsp_metrics("r1", "r1.00-00", [62])
+
+ # Remove advertise high metrics
+ r1.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ no advertise-high-metrics
+ """
+ )
+
+ # Confirm low metrics values on each isis interface on r1
+ check_interface_metrics("r1", [10, 20])
+
+ # Confirm low metrics values within isis database on r1
+ check_lsp_metrics("r1", "r1.00-00", [10, 20])
+
+ # Remove narrow metric-style
+ r1.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ no metric-style transition
+ """
+ )
+
+
+def test_isis_advertise_high_metrics_route():
+ """
+ Topology:
+
+ r2
+ // \\
+ r1 r4
+ \\ //
+ r3
+
+ Devices are configured with preferred route between r1 and r4:
+ r1 -> r2 -> r4
+ Configure "advertise-high-metrics" on r2 and check that preferred route is:
+ r1 -> r3 -> r4.
+ Shut r3 and check that preferred route is:
+ r1 -> r2 -> r4.
+ """
+ tgen = get_topogen()
+ net = get_topogen().net
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Testing advertise high metrics route behavior")
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ # Verify the preferred path from r1 to r4 (192.168.1.6) is currently via 192.168.1.1, eth-r2
+ check_ip_route("r1", "192.168.1.6/31", "eth-r2")
+
+ # Configure advertise high metrics on r2
+ r2.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ advertise-high-metrics
+ """
+ )
+
+ # Verify the preferred path from r1 to r4 (192.168.1.6) is now via 192.168.1.3, eth-r3
+ check_ip_route("r1", "192.168.1.6/31", "eth-r3")
+
+ # Shutdown r3
+ logger.info("Stop router r3")
+ stop_router(tgen, "r3")
+
+ # Verify the preferred path from r1 to r4 (192.168.1.6) is now via 192.168.1.1, eth-r2
+ check_ip_route("r1", "192.168.1.6/31", "eth-r2")
+
+ # Start r3
+ logger.info("Start router r3")
+ start_router(tgen, "r3")
+
+
+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_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/bfdd.conf b/tests/topotests/isis_lfa_topo1/rt1/bfdd.conf
new file mode 100644
index 0000000..ef67eae
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/bfdd.conf
@@ -0,0 +1,4 @@
+hostname rt1
+!
+bfd
+!
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..fc81df0
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/isisd.conf
@@ -0,0 +1,57 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt3
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt4
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt5
+ ipv6 router isis 1
+ isis metric 20
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt6
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..92dd7b5
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "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/step14/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step14/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step14/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step15/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step15/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..a00d2d3
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step15/show_ipv6_route.ref.diff
@@ -0,0 +1,50 @@
+--- a/rt1/step14/show_ipv6_route.ref
++++ b/rt1/step15/show_ipv6_route.ref
+@@ -6,22 +6,12 @@
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+- "metric":20,
++ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+@@ -151,22 +141,12 @@
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+- "metric":25,
++ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step16/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step16/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..5e48511
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step16/show_ipv6_route.ref.diff
@@ -0,0 +1,53 @@
+--- a/rt1/step15/show_ipv6_route.ref
++++ b/rt1/step16/show_ipv6_route.ref
+@@ -32,16 +32,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+@@ -90,16 +80,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+@@ -119,16 +99,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+ "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/bfdd.conf b/tests/topotests/isis_lfa_topo1/rt2/bfdd.conf
new file mode 100644
index 0000000..25fa0d8
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt2/bfdd.conf
@@ -0,0 +1,4 @@
+hostname rt2
+!
+bfd
+!
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..6981692
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt2/isisd.conf
@@ -0,0 +1,42 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt3
+ ipv6 router isis 1
+ isis metric 5
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis metric 5
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..236a41d
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "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..e3ddb09
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt3/isisd.conf
@@ -0,0 +1,41 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt2
+ ipv6 router isis 1
+ isis metric 5
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..290ebb9
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "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..4db5c8e
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt4/isisd.conf
@@ -0,0 +1,34 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis metric 15
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..1f99ad1
--- /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": 10,
+ "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": 10,
+ "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..1206a4e
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt5/isisd.conf
@@ -0,0 +1,34 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..1f99ad1
--- /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": 10,
+ "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": 10,
+ "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..2ba9e49
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt6/isisd.conf
@@ -0,0 +1,33 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..1f99ad1
--- /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": 10,
+ "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": 10,
+ "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..060be2b
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt7/isisd.conf
@@ -0,0 +1,56 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt3
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt4
+ ipv6 router isis 1
+ isis metric 15
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt5
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute lfa
+!
+interface eth-rt6
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..92dd7b5
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "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..44b3dd0
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/test_isis_lfa_topo1.py
@@ -0,0 +1,1086 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_isis_tilfa_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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 time
+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, 16 + 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))
+ )
+ router.load_config(
+ TopoRouter.RD_BFD, os.path.join(CWD, "/dev/null".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, wait=0.5, count=120):
+ "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=count, wait=wait)
+ 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"],
+ )
+
+
+#
+# Step 14
+#
+# Action(s):
+# - Setting spf-delay-ietf init-delay of 15s
+#
+# Expected changes:
+# - No routing table change
+# - At the end of test, SPF reacts to a failure in 15s
+#
+def test_rib_ipv6_step14():
+ logger.info("Test (step 14): 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("Setting spf-delay-ietf init-delay of 15s")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "spf-delay-ietf init-delay 15000 short-delay 0 long-delay 0 holddown 0 time-to-learn 0"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][14]["show_ipv6_route.ref"],
+ )
+
+
+#
+# Step 15
+#
+# Action(s):
+# - shut the eth-rt2 interface on rt1
+#
+# Expected changes:
+# - Route switchover of routes via eth-rt2
+#
+def test_rib_ipv6_step15():
+ logger.info("Test (step 15): 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("Shut the interface to rt2 from the switch side and check fast-reroute")
+ tgen.net.cmd_raises("ip link set %s down" % tgen.net["s1"].intfs[0])
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][15]["show_ipv6_route.ref"],
+ count=10,
+ wait=0.5,
+ )
+
+
+#
+# Step 16
+#
+# Action(s): wait for the convergence and SPF computation on rt1
+#
+# Expected changes:
+# - convergence of IPv6 RIB
+#
+def test_rib_ipv6_step16():
+ logger.info("Test (step 16): 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("Check SPF convergence")
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][16]["show_ipv6_route.ref"],
+ )
+
+
+#
+# Step 17
+#
+# Action(s):
+# - Unshut the interface to rt2 from the switch sid
+#
+# Expected changes:
+# - The routing table converges
+#
+def test_rib_ipv6_step17():
+ logger.info("Test (step 17): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ rname = "rt1"
+
+ logger.info("Unsetting spf-delay-ietf init-delay of 15s")
+ tgen.net[rname].cmd('vtysh -c "conf t" -c "router isis 1" -c "no spf-delay-ietf"')
+
+ logger.info(
+ "Unshut the interface to rt2 from the switch side and check fast-reroute"
+ )
+ tgen.net.cmd_raises("ip link set %s up" % tgen.net["s1"].intfs[0])
+
+ logger.info("Setting spf-delay-ietf init-delay of 15s")
+ tgen.net[rname].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "spf-delay-ietf init-delay 15000 short-delay 0 long-delay 0 holddown 0 time-to-learn 0"'
+ )
+
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][14]["show_ipv6_route.ref"],
+ )
+
+
+#
+# Step 18
+#
+# Action(s):
+# - drop traffic between rt1 and rt2 by shutting down the bridge between
+# the routers. Interfaces on rt1 and rt2 stay up.
+#
+#
+# Expected changes:
+# - Route switchover of routes via eth-rt2
+#
+def test_rib_ipv6_step18():
+ def _rt2_neigh_down(router):
+ output = json.loads(router.vtysh_cmd("show isis neighbor rt2 json"))
+
+ """
+ Previous output was:
+ {
+ "areas":[
+ {
+ "area":"1",
+ "circuits":[
+ {
+ "circuit":0,
+ "adj":"rt2",
+ "interface":{
+ "name":"eth-rt2",
+ "state":"Up",
+ "adj-flaps":1,
+ "last-ago":"21s",
+ "circuit-type":"L1",
+ "speaks":"IPv6",
+ "topologies":{
+ "topo-0":"ipv6-unicast"
+ },
+ "snpa":"2020.2020.2020",
+ "area-address":{
+ "isonet":"49.0000"
+ },
+ "ipv6-link-local":{
+ "ipv6":"fe80::ac19:a8ff:fee5:f48f"
+ },
+ "adj-sid":{
+ }
+ },
+ "level":1,
+ "expires-in":"2s"
+ },
+ {
+ "circuit":0
+ },
+ {
+ "circuit":0
+ },
+ {
+ "circuit":0
+ },
+ {
+ "circuit":0
+ },
+ {
+ "circuit":0
+ }
+ ]
+ }
+ ]
+ """
+
+ expected = {
+ "areas": [
+ {
+ "area": "1",
+ "circuits": [
+ {"circuit": 0},
+ {"circuit": 0},
+ {"circuit": 0},
+ {"circuit": 0},
+ {"circuit": 0},
+ {"circuit": 0},
+ ],
+ }
+ ]
+ }
+
+ return topotest.json_cmp(output, expected, exact=True)
+
+ logger.info("Test (step 18): 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("Drop traffic between rt1 and rt2")
+ tgen.net.cmd_raises("ip link set s1 down")
+
+ rname = "rt1"
+ router = tgen.gears[rname]
+ test_func = partial(_rt2_neigh_down, router)
+ success, result = topotest.run_and_expect(test_func, None, count=200, wait=0.05)
+ assert result is None, 'rt2 neighbor is still present on "{}"'.format(router)
+
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][15]["show_ipv6_route.ref"],
+ count=10,
+ wait=0.5,
+ )
+
+
+#
+# Step 19
+#
+# Action(s): wait for the convergence and SPF computation on rt1
+#
+# Expected changes:
+# - convergence of IPv6 RIB
+#
+def test_rib_ipv6_step19():
+ logger.info("Test (step 19): 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("Check SPF convergence")
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][16]["show_ipv6_route.ref"],
+ )
+
+
+#
+# Step 20
+#
+# Action(s):
+# - Unshut the switch from rt1 to rt2
+#
+# Expected changes:
+# - The routing table goes back to the nominal state
+#
+def test_rib_ipv6_step20():
+ logger.info("Test (step 20): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ rname = "rt1"
+
+ logger.info("Unsetting spf-delay-ietf init-delay of 15s")
+ tgen.net[rname].cmd('vtysh -c "conf t" -c "router isis 1" -c "no spf-delay-ietf"')
+
+ logger.info(
+ "Unshut the interface to rt2 from the switch side and check fast-reroute"
+ )
+ tgen.net.cmd_raises("ip link set s1 up")
+
+ logger.info("Setting spf-delay-ietf init-delay of 15s")
+ tgen.net[rname].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "spf-delay-ietf init-delay 15000 short-delay 0 long-delay 0 holddown 0 time-to-learn 0"'
+ )
+
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][14]["show_ipv6_route.ref"],
+ )
+
+
+#
+# Step 21
+#
+# Action(s):
+# - clear the rt2 ISIS neighbor on rt1
+#
+# Expected changes:
+# - Route switchover of routes via eth-rt2
+#
+def test_rib_ipv6_step21():
+ logger.info("Test (step 21): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ rname = "rt1"
+
+ logger.info("Clear the rt2 ISIS neighbor on rt1 and check fast-reroute")
+ tgen.gears[rname].vtysh_cmd("clear isis neighbor rt2")
+
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][15]["show_ipv6_route.ref"],
+ count=10,
+ wait=0.5,
+ )
+
+
+#
+# Step 22
+#
+# Action(s): wait for the convergence and SPF computation on rt1
+#
+# Expected changes:
+# - convergence of IPv6 RIB
+#
+def test_rib_ipv6_step22():
+ logger.info("Test (step 22): 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("Check SPF convergence")
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][16]["show_ipv6_route.ref"],
+ )
+
+
+#
+# Step 23
+#
+# Action(s):
+# - Setting BFD
+#
+# Expected changes:
+# - No routing table change
+# - BFD comes up
+#
+def test_rib_ipv6_step23():
+ logger.info("Test (step 23): 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("Setup BFD on rt1 and rt2")
+ for rname in ["rt1", "rt2"]:
+ conf_file = os.path.join(CWD, "{}/bfdd.conf".format(rname))
+ tgen.net[rname].cmd("vtysh -f {}".format(conf_file))
+
+ logger.info("Set ISIS BFD")
+ tgen.net["rt1"].cmd('vtysh -c "conf t" -c "int eth-rt2" -c "isis bfd"')
+ tgen.net["rt2"].cmd('vtysh -c "conf t" -c "int eth-rt1" -c "isis bfd"')
+
+ rname = "rt1"
+ expect = '[{"multihop":false,"interface":"eth-rt2","status":"up"}]'
+ router_compare_json_output(rname, "show bfd peers json", expect)
+
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][14]["show_ipv6_route.ref"],
+ )
+
+
+#
+# Step 24
+#
+# Action(s):
+# - drop traffic between rt1 and rt2 by shutting down the bridge between
+# the routers. Interfaces on rt1 and rt2 stay up.
+#
+# Expected changes:
+# - BFD comes down before IS-IS
+# - Route switchover of routes via eth-rt2
+#
+def test_rib_ipv6_step24():
+ def _bfd_down(router):
+ output = json.loads(router.vtysh_cmd("show bfd peers json"))
+ expected = []
+ return topotest.json_cmp(output, expected, exact=True)
+
+ logger.info("Test (step 24): 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("Shut the interface to rt2 from the switch side and check fast-reroute")
+ tgen.net.cmd_raises("ip link set s1 down")
+
+ rname = "rt1"
+ router = tgen.gears[rname]
+ test_func = partial(_bfd_down, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.3)
+ assert result is None, 'BFD session is still up on "{}"'.format(router)
+
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][15]["show_ipv6_route.ref"],
+ count=10,
+ )
+
+
+#
+# Step 25
+#
+# Action(s): wait for the convergence and SPF computation on rt1
+#
+# Expected changes:
+# - convergence of IPv6 RIB
+#
+def test_rib_ipv6_step25():
+ logger.info("Test (step 25): 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("Check SPF convergence")
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][16]["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..51bf7e6
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt1/isisd.conf
@@ -0,0 +1,28 @@
+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-interval 1
+ isis hello-multiplier 10
+ 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..9c5901b
--- /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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "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..96c9f33
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt2/isisd.conf
@@ -0,0 +1,37 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt4
+ ip router isis 2
+ ipv6 router isis 2
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..b56fe12
--- /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": 10,
+ "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": 10,
+ "neighbor-priority": 100,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-2",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 10,
+ "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..c3354f2
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt3/isisd.conf
@@ -0,0 +1,37 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt5
+ ip router isis 2
+ ipv6 router isis 2
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..279d4dc
--- /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": 10,
+ "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": 10,
+ "neighbor-priority": 100,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "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..f1627e7
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt4/isisd.conf
@@ -0,0 +1,45 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt5
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt6
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..39ee612
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "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..4124159
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt5/isisd.conf
@@ -0,0 +1,45 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt4
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt6
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..a6f08c2
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "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..b66f40e
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt6/isisd.conf
@@ -0,0 +1,34 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt5
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..f5d9aa7
--- /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": 10,
+ "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": 10,
+ "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..c4ce6a3
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/test_isis_lsp_bits_topo1.py
@@ -0,0 +1,364 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_isis_lsp_bits_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by Volta Networks
+#
+
+"""
+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..c96d0b1
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/isisd.conf
@@ -0,0 +1,41 @@
+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-interval 1
+ isis hello-multiplier 10
+ 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-interval 1
+ isis hello-multiplier 10
+ 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..4c5a31d
--- /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": 10,
+ "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": 10,
+ "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..2720337
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt2/isisd.conf
@@ -0,0 +1,34 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis network point-to-point
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..1139d9d
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt3/isisd.conf
@@ -0,0 +1,34 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis network point-to-point
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..980bd5d
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt4/isisd.conf
@@ -0,0 +1,34 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis network point-to-point
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..82ba9cb
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt5/isisd.conf
@@ -0,0 +1,34 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis network point-to-point
+!
+interface eth-rt7
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..ea859ff
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt6/isisd.conf
@@ -0,0 +1,34 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis network point-to-point
+!
+interface eth-rt8
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..5acfa95
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt7/isisd.conf
@@ -0,0 +1,34 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis network point-to-point
+!
+interface eth-rt8
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..2377842
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt8/isisd.conf
@@ -0,0 +1,34 @@
+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-interval 1
+ isis hello-multiplier 10
+ isis network point-to-point
+!
+interface eth-rt7
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..15327fe
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/test_isis_rlfa_topo1.py
@@ -0,0 +1,644 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_isis_rlfa_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..64f51fc
--- /dev/null
+++ b/tests/topotests/isis_snmp/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 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-eth0
+ !
+ interface r1-eth1
+ !
+ !
+!
+line vty
+!
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..cdcd9a2
--- /dev/null
+++ b/tests/topotests/isis_snmp/r1/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp: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/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_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..e511929
--- /dev/null
+++ b/tests/topotests/isis_snmp/r2/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp::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
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_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..19fb256
--- /dev/null
+++ b/tests/topotests/isis_snmp/r3/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp: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
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_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..2178a3e
--- /dev/null
+++ b/tests/topotests/isis_snmp/r4/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp: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
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..b3d10b0
--- /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_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..5b11cfc
--- /dev/null
+++ b/tests/topotests/isis_snmp/r5/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp::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
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..ddef080
--- /dev/null
+++ b/tests/topotests/isis_snmp/test_isis_snmp.py
@@ -0,0 +1,361 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_isis_snmp.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+"""
+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_flex_algo_topo1/rt1/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf
new file mode 100644
index 0000000..090e896
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf
@@ -0,0 +1,96 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+!debug northbound
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map red bit-position 0
+affinity-map blue bit-position 1
+affinity-map green bit-position 2
+affinity-map yellow bit-position 3
+affinity-map orange bit-position 4
+!
+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 10
+ isis network point-to-point
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0001.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 201
+ dataplane sr-mpls
+ advertise-definition
+ affinity exclude-any red
+ !
+ flex-algo 202
+ dataplane sr-mpls
+ advertise-definition
+ affinity exclude-any blue
+ !
+ flex-algo 203
+ dataplane sr-mpls
+ advertise-definition
+ affinity exclude-any green
+ !
+ flex-algo 204
+ dataplane sr-mpls
+ advertise-definition
+ affinity include-any blue green
+ !
+ flex-algo 205
+ dataplane sr-mpls
+ advertise-definition
+ affinity include-any red green
+ !
+ flex-algo 206
+ dataplane sr-mpls
+ advertise-definition
+ affinity include-any red blue
+ !
+ flex-algo 207
+ dataplane sr-mpls
+ advertise-definition
+ affinity include-all yellow orange
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.1/32 index 1
+ segment-routing prefix 1.1.1.1/32 algorithm 201 index 101
+ segment-routing prefix 1.1.1.1/32 algorithm 202 index 201
+ segment-routing prefix 1.1.1.1/32 algorithm 203 index 301
+ segment-routing prefix 1.1.1.1/32 algorithm 204 index 401
+ segment-routing prefix 1.1.1.1/32 algorithm 205 index 501
+ segment-routing prefix 1.1.1.1/32 algorithm 206 index 601
+ segment-routing prefix 1.1.1.1/32 algorithm 207 index 701
+ segment-routing prefix 2001:db8:1000::1/128 index 1001
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 201 index 1101
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 202 index 1201
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 204 index 1401
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 205 index 1501
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 206 index 1601
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 207 index 1701
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref
new file mode 100644
index 0000000..750abc1
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set \ No newline at end of file
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref
new file mode 100644
index 0000000..2b16c53
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20402,
+ "outLabelStack":[
+ 20402
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20603,
+ "outLabelStack":[
+ 20603
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20702,
+ "outLabelStack":[
+ 20702
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21102,
+ "outLabelStack":[
+ 21102
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21303,
+ "outLabelStack":[
+ 21303
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21402,
+ "outLabelStack":[
+ 21402
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21603,
+ "outLabelStack":[
+ 21603
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21702,
+ "outLabelStack":[
+ 21702
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref
new file mode 100644
index 0000000..9421990
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref
new file mode 100644
index 0000000..2b16c53
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20402,
+ "outLabelStack":[
+ 20402
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20603,
+ "outLabelStack":[
+ 20603
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20702,
+ "outLabelStack":[
+ 20702
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21102,
+ "outLabelStack":[
+ 21102
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21303,
+ "outLabelStack":[
+ 21303
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21402,
+ "outLabelStack":[
+ 21402
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21603,
+ "outLabelStack":[
+ 21603
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21702,
+ "outLabelStack":[
+ 21702
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref
new file mode 100644
index 0000000..9421990
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref
new file mode 100644
index 0000000..2b16c53
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20402,
+ "outLabelStack":[
+ 20402
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20603,
+ "outLabelStack":[
+ 20603
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20702,
+ "outLabelStack":[
+ 20702
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21102,
+ "outLabelStack":[
+ 21102
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21303,
+ "outLabelStack":[
+ 21303
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21402,
+ "outLabelStack":[
+ 21402
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21603,
+ "outLabelStack":[
+ 21603
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21702,
+ "outLabelStack":[
+ 21702
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref
new file mode 100644
index 0000000..9421990
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref
new file mode 100644
index 0000000..2b16c53
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20402,
+ "outLabelStack":[
+ 20402
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20603,
+ "outLabelStack":[
+ 20603
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20702,
+ "outLabelStack":[
+ 20702
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21102,
+ "outLabelStack":[
+ 21102
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21303,
+ "outLabelStack":[
+ 21303
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21402,
+ "outLabelStack":[
+ 21402
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21603,
+ "outLabelStack":[
+ 21603
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21702,
+ "outLabelStack":[
+ 21702
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref
new file mode 100644
index 0000000..13a9616
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref
@@ -0,0 +1,115 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: Not found
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref
new file mode 100644
index 0000000..d311e92
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref
@@ -0,0 +1,450 @@
+{
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20402,
+ "outLabelStack":[
+ 20402
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20603,
+ "outLabelStack":[
+ 20603
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20702,
+ "outLabelStack":[
+ 20702
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21102,
+ "outLabelStack":[
+ 21102
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21402,
+ "outLabelStack":[
+ 21402
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21603,
+ "outLabelStack":[
+ 21603
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21702,
+ "outLabelStack":[
+ 21702
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref
new file mode 100644
index 0000000..9421990
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref
new file mode 100644
index 0000000..2b16c53
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20402,
+ "outLabelStack":[
+ 20402
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20603,
+ "outLabelStack":[
+ 20603
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20702,
+ "outLabelStack":[
+ 20702
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21102,
+ "outLabelStack":[
+ 21102
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21303,
+ "outLabelStack":[
+ 21303
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21402,
+ "outLabelStack":[
+ 21402
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21603,
+ "outLabelStack":[
+ 21603
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21702,
+ "outLabelStack":[
+ 21702
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref
new file mode 100644
index 0000000..9421990
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref
new file mode 100644
index 0000000..2b16c53
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20402,
+ "outLabelStack":[
+ 20402
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20603,
+ "outLabelStack":[
+ 20603
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20702,
+ "outLabelStack":[
+ 20702
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21102,
+ "outLabelStack":[
+ 21102
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21303,
+ "outLabelStack":[
+ 21303
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21402,
+ "outLabelStack":[
+ 21402
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21603,
+ "outLabelStack":[
+ 21603
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21702,
+ "outLabelStack":[
+ 21702
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref
new file mode 100644
index 0000000..ce31766
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref
@@ -0,0 +1,112 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: None
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref
new file mode 100644
index 0000000..d311e92
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref
@@ -0,0 +1,450 @@
+{
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20402,
+ "outLabelStack":[
+ 20402
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20603,
+ "outLabelStack":[
+ 20603
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20702,
+ "outLabelStack":[
+ 20702
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21102,
+ "outLabelStack":[
+ 21102
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21402,
+ "outLabelStack":[
+ 21402
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21603,
+ "outLabelStack":[
+ 21603
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21702,
+ "outLabelStack":[
+ 21702
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref
new file mode 100644
index 0000000..e56e5af
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref
@@ -0,0 +1,108 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref
new file mode 100644
index 0000000..d311e92
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref
@@ -0,0 +1,450 @@
+{
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20402,
+ "outLabelStack":[
+ 20402
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20603,
+ "outLabelStack":[
+ 20603
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20702,
+ "outLabelStack":[
+ 20702
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21102,
+ "outLabelStack":[
+ 21102
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21402,
+ "outLabelStack":[
+ 21402
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21603,
+ "outLabelStack":[
+ 21603
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21702,
+ "outLabelStack":[
+ 21702
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref
new file mode 100644
index 0000000..9421990
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref
new file mode 100644
index 0000000..2b16c53
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20402,
+ "outLabelStack":[
+ 20402
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20603,
+ "outLabelStack":[
+ 20603
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20702,
+ "outLabelStack":[
+ 20702
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21102,
+ "outLabelStack":[
+ 21102
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21303,
+ "outLabelStack":[
+ 21303
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21402,
+ "outLabelStack":[
+ 21402
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21603,
+ "outLabelStack":[
+ 21603
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21702,
+ "outLabelStack":[
+ 21702
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref
new file mode 100644
index 0000000..9421990
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref
new file mode 100644
index 0000000..2b16c53
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20402,
+ "outLabelStack":[
+ 20402
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20603,
+ "outLabelStack":[
+ 20603
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20702,
+ "outLabelStack":[
+ 20702
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.3"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21102,
+ "outLabelStack":[
+ 21102
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21303,
+ "outLabelStack":[
+ 21303
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21402,
+ "outLabelStack":[
+ 21402
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21603,
+ "outLabelStack":[
+ 21603
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21702,
+ "outLabelStack":[
+ 21702
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..5140eda
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf
@@ -0,0 +1,39 @@
+log file zebra.log
+!
+hostname rt1
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map red bit-position 0
+affinity-map blue bit-position 1
+affinity-map green bit-position 2
+affinity-map yellow bit-position 3
+affinity-map orange bit-position 4
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address 2001:db8:1000::1/128
+!
+interface eth-rt2
+ ip address 10.12.0.1/24
+ link-params
+ affinity red
+ exit-link-params
+!
+interface eth-rt3
+ ip address 10.13.0.1/24
+ link-params
+ affinity green yellow orange
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf
new file mode 100644
index 0000000..3901cca
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf
@@ -0,0 +1,96 @@
+password 1
+hostname rt2
+log file isisd.log
+!
+!debug northbound
+!debug isis events
+!debug isis route-events
+!debug isis spf-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map red bit-position 0
+affinity-map blue bit-position 1
+affinity-map green bit-position 2
+affinity-map yellow bit-position 3
+affinity-map orange bit-position 4
+!
+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 10
+ isis network point-to-point
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0002.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 201
+ dataplane sr-mpls
+ advertise-definition
+ affinity exclude-any red
+ !
+ flex-algo 202
+ dataplane sr-mpls
+ advertise-definition
+ affinity exclude-any blue
+ !
+ flex-algo 203
+ dataplane sr-mpls
+ advertise-definition
+ affinity exclude-any green
+ !
+ flex-algo 204
+ dataplane sr-mpls
+ advertise-definition
+ affinity include-any blue green
+ !
+ flex-algo 205
+ dataplane sr-mpls
+ advertise-definition
+ affinity include-any red green
+ !
+ flex-algo 206
+ dataplane sr-mpls
+ advertise-definition
+ affinity include-any red blue
+ !
+ flex-algo 207
+ dataplane sr-mpls
+ advertise-definition
+ affinity include-all yellow orange
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 2.2.2.2/32 index 2
+ segment-routing prefix 2.2.2.2/32 algorithm 201 index 102
+ segment-routing prefix 2.2.2.2/32 algorithm 202 index 202
+ segment-routing prefix 2.2.2.2/32 algorithm 203 index 302
+ segment-routing prefix 2.2.2.2/32 algorithm 204 index 402
+ segment-routing prefix 2.2.2.2/32 algorithm 205 index 502
+ segment-routing prefix 2.2.2.2/32 algorithm 206 index 602
+ segment-routing prefix 2.2.2.2/32 algorithm 207 index 702
+ segment-routing prefix 2001:db8:1000::2/128 index 1002
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 201 index 1102
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 202 index 1202
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 203 index 1302
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 204 index 1402
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 205 index 1502
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 206 index 1602
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 207 index 1702
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref
new file mode 100644
index 0000000..defa3ef
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref
new file mode 100644
index 0000000..099045a
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20203,
+ "outLabelStack":[
+ 20203
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20401,
+ "outLabelStack":[
+ 20401
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20503,
+ "outLabelStack":[
+ 20503
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20701,
+ "outLabelStack":[
+ 20701
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21101,
+ "outLabelStack":[
+ 21101
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21203,
+ "outLabelStack":[
+ 21203
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21301":{
+ "inLabel":21301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21401,
+ "outLabelStack":[
+ 21401
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21503,
+ "outLabelStack":[
+ 21503
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21701,
+ "outLabelStack":[
+ 21701
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref
new file mode 100644
index 0000000..defa3ef
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref
new file mode 100644
index 0000000..099045a
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20203,
+ "outLabelStack":[
+ 20203
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20401,
+ "outLabelStack":[
+ 20401
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20503,
+ "outLabelStack":[
+ 20503
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20701,
+ "outLabelStack":[
+ 20701
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21101,
+ "outLabelStack":[
+ 21101
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21203,
+ "outLabelStack":[
+ 21203
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21301":{
+ "inLabel":21301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21401,
+ "outLabelStack":[
+ 21401
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21503,
+ "outLabelStack":[
+ 21503
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21701,
+ "outLabelStack":[
+ 21701
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref
new file mode 100644
index 0000000..defa3ef
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref
new file mode 100644
index 0000000..0ed0eb3
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20203,
+ "outLabelStack":[
+ 20203
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20311":{
+ "inLabel":20311,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20401,
+ "outLabelStack":[
+ 20401
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20503,
+ "outLabelStack":[
+ 20503
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20701,
+ "outLabelStack":[
+ 20701
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21101,
+ "outLabelStack":[
+ 21101
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21203,
+ "outLabelStack":[
+ 21203
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21311":{
+ "inLabel":21311,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21401,
+ "outLabelStack":[
+ 21401
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21503,
+ "outLabelStack":[
+ 21503
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21701,
+ "outLabelStack":[
+ 21701
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref
new file mode 100644
index 0000000..defa3ef
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref
new file mode 100644
index 0000000..099045a
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20203,
+ "outLabelStack":[
+ 20203
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20401,
+ "outLabelStack":[
+ 20401
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20503,
+ "outLabelStack":[
+ 20503
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20701,
+ "outLabelStack":[
+ 20701
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21101,
+ "outLabelStack":[
+ 21101
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21203,
+ "outLabelStack":[
+ 21203
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21301":{
+ "inLabel":21301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21401,
+ "outLabelStack":[
+ 21401
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21503,
+ "outLabelStack":[
+ 21503
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21701,
+ "outLabelStack":[
+ 21701
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref
new file mode 100644
index 0000000..b10cb70
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref
@@ -0,0 +1,114 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: Not found
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref
new file mode 100644
index 0000000..8b40738
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref
@@ -0,0 +1,450 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20203,
+ "outLabelStack":[
+ 20203
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20401,
+ "outLabelStack":[
+ 20401
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20503,
+ "outLabelStack":[
+ 20503
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20701,
+ "outLabelStack":[
+ 20701
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21101,
+ "outLabelStack":[
+ 21101
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21203,
+ "outLabelStack":[
+ 21203
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21401,
+ "outLabelStack":[
+ 21401
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21503,
+ "outLabelStack":[
+ 21503
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21701,
+ "outLabelStack":[
+ 21701
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref
new file mode 100644
index 0000000..defa3ef
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref
new file mode 100644
index 0000000..099045a
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20203,
+ "outLabelStack":[
+ 20203
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20401,
+ "outLabelStack":[
+ 20401
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20503,
+ "outLabelStack":[
+ 20503
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20701,
+ "outLabelStack":[
+ 20701
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21101,
+ "outLabelStack":[
+ 21101
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21203,
+ "outLabelStack":[
+ 21203
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21301":{
+ "inLabel":21301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21401,
+ "outLabelStack":[
+ 21401
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21503,
+ "outLabelStack":[
+ 21503
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21701,
+ "outLabelStack":[
+ 21701
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref
new file mode 100644
index 0000000..defa3ef
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref
new file mode 100644
index 0000000..099045a
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20203,
+ "outLabelStack":[
+ 20203
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20401,
+ "outLabelStack":[
+ 20401
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20503,
+ "outLabelStack":[
+ 20503
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20701,
+ "outLabelStack":[
+ 20701
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21101,
+ "outLabelStack":[
+ 21101
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21203,
+ "outLabelStack":[
+ 21203
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21301":{
+ "inLabel":21301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21401,
+ "outLabelStack":[
+ 21401
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21503,
+ "outLabelStack":[
+ 21503
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21701,
+ "outLabelStack":[
+ 21701
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref
new file mode 100644
index 0000000..358c431
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref
@@ -0,0 +1,111 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: None
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref
new file mode 100644
index 0000000..8b40738
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref
@@ -0,0 +1,450 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20203,
+ "outLabelStack":[
+ 20203
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20401,
+ "outLabelStack":[
+ 20401
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20503,
+ "outLabelStack":[
+ 20503
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20701,
+ "outLabelStack":[
+ 20701
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21101,
+ "outLabelStack":[
+ 21101
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21203,
+ "outLabelStack":[
+ 21203
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21401,
+ "outLabelStack":[
+ 21401
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21503,
+ "outLabelStack":[
+ 21503
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21701,
+ "outLabelStack":[
+ 21701
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref
new file mode 100644
index 0000000..04d07e6
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref
@@ -0,0 +1,107 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref
new file mode 100644
index 0000000..8b40738
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref
@@ -0,0 +1,450 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20203,
+ "outLabelStack":[
+ 20203
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20401,
+ "outLabelStack":[
+ 20401
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20503,
+ "outLabelStack":[
+ 20503
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20701,
+ "outLabelStack":[
+ 20701
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21101,
+ "outLabelStack":[
+ 21101
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21203,
+ "outLabelStack":[
+ 21203
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21401,
+ "outLabelStack":[
+ 21401
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21503,
+ "outLabelStack":[
+ 21503
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21701,
+ "outLabelStack":[
+ 21701
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref
new file mode 100644
index 0000000..defa3ef
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref
new file mode 100644
index 0000000..099045a
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20203,
+ "outLabelStack":[
+ 20203
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20401,
+ "outLabelStack":[
+ 20401
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20503,
+ "outLabelStack":[
+ 20503
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20701,
+ "outLabelStack":[
+ 20701
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21101,
+ "outLabelStack":[
+ 21101
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21203,
+ "outLabelStack":[
+ 21203
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21301":{
+ "inLabel":21301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21401,
+ "outLabelStack":[
+ 21401
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21503,
+ "outLabelStack":[
+ 21503
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21701,
+ "outLabelStack":[
+ 21701
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref
new file mode 100644
index 0000000..defa3ef
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: yes
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref
new file mode 100644
index 0000000..3db0ebf
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref
@@ -0,0 +1,482 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20203":{
+ "inLabel":20203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20203,
+ "outLabelStack":[
+ 20203
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20401,
+ "outLabelStack":[
+ 20401
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20403":{
+ "inLabel":20403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20503":{
+ "inLabel":20503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20503,
+ "outLabelStack":[
+ 20503
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20603":{
+ "inLabel":20603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20701,
+ "outLabelStack":[
+ 20701
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20703":{
+ "inLabel":20703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21003":{
+ "inLabel":21003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21101,
+ "outLabelStack":[
+ 21101
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21103":{
+ "inLabel":21103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21203":{
+ "inLabel":21203,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21203,
+ "outLabelStack":[
+ 21203
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21303":{
+ "inLabel":21303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21401,
+ "outLabelStack":[
+ 21401
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21403":{
+ "inLabel":21403,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21503":{
+ "inLabel":21503,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21503,
+ "outLabelStack":[
+ 21503
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21603":{
+ "inLabel":21603,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21701,
+ "outLabelStack":[
+ 21701
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ },
+ "21703":{
+ "inLabel":21703,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..388348f
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf
@@ -0,0 +1,39 @@
+log file zebra.log
+!
+hostname rt2
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map red bit-position 0
+affinity-map blue bit-position 1
+affinity-map green bit-position 2
+affinity-map yellow bit-position 3
+affinity-map orange bit-position 4
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address 2001:db8:1000::2/128
+!
+interface eth-rt1
+ ip address 10.12.0.2/24
+ link-params
+ affinity red
+ exit-link-params
+!
+interface eth-rt3
+ ip address 10.23.0.2/24
+ link-params
+ affinity blue yellow orange
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf
new file mode 100644
index 0000000..f7a52bc
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf
@@ -0,0 +1,76 @@
+password 1
+hostname rt3
+log file isisd.log
+!
+!debug northbound
+!debug isis events
+!debug isis route-events
+!debug isis spf-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map red bit-position 0
+affinity-map blue bit-position 1
+affinity-map green bit-position 2
+affinity-map yellow bit-position 3
+affinity-map orange bit-position 4
+!
+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 10
+ isis network point-to-point
+!
+interface eth-rt2
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0003.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 201
+ dataplane sr-mpls
+ flex-algo 202
+ dataplane sr-mpls
+ flex-algo 203
+ dataplane sr-mpls
+ flex-algo 204
+ dataplane sr-mpls
+ flex-algo 205
+ dataplane sr-mpls
+ flex-algo 206
+ dataplane sr-mpls
+ flex-algo 207
+ dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 3.3.3.3/32 index 3
+ segment-routing prefix 3.3.3.3/32 algorithm 201 index 103
+ segment-routing prefix 3.3.3.3/32 algorithm 202 index 203
+ segment-routing prefix 3.3.3.3/32 algorithm 203 index 303
+ segment-routing prefix 3.3.3.3/32 algorithm 204 index 403
+ segment-routing prefix 3.3.3.3/32 algorithm 205 index 503
+ segment-routing prefix 3.3.3.3/32 algorithm 206 index 603
+ segment-routing prefix 3.3.3.3/32 algorithm 207 index 703
+ segment-routing prefix 2001:db8:1000::3/128 index 1003
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 201 index 1103
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 202 index 1203
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 203 index 1303
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 204 index 1403
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 205 index 1503
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 206 index 1603
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 207 index 1703
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref
new file mode 100644
index 0000000..7954734
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set \ No newline at end of file
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref
new file mode 100644
index 0000000..9ab0c74
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20202,
+ "outLabelStack":[
+ 20202
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20301,
+ "outLabelStack":[
+ 20301
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20502,
+ "outLabelStack":[
+ 20502
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20601,
+ "outLabelStack":[
+ 20601
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21202,
+ "outLabelStack":[
+ 21202
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21301":{
+ "inLabel":21301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21301,
+ "outLabelStack":[
+ 21301
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21502,
+ "outLabelStack":[
+ 21502
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21601,
+ "outLabelStack":[
+ 21601
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref
new file mode 100644
index 0000000..85182db
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref
new file mode 100644
index 0000000..9ab0c74
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20202,
+ "outLabelStack":[
+ 20202
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20301,
+ "outLabelStack":[
+ 20301
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20502,
+ "outLabelStack":[
+ 20502
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20601,
+ "outLabelStack":[
+ 20601
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21202,
+ "outLabelStack":[
+ 21202
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21301":{
+ "inLabel":21301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21301,
+ "outLabelStack":[
+ 21301
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21502,
+ "outLabelStack":[
+ 21502
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21601,
+ "outLabelStack":[
+ 21601
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref
new file mode 100644
index 0000000..85182db
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref
new file mode 100644
index 0000000..57755b0
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20202,
+ "outLabelStack":[
+ 20202
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20311":{
+ "inLabel":20311,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20311,
+ "outLabelStack":[
+ 20311
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20502,
+ "outLabelStack":[
+ 20502
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20601,
+ "outLabelStack":[
+ 20601
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21202,
+ "outLabelStack":[
+ 21202
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21311":{
+ "inLabel":21311,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21311,
+ "outLabelStack":[
+ 21311
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21502,
+ "outLabelStack":[
+ 21502
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21601,
+ "outLabelStack":[
+ 21601
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref
new file mode 100644
index 0000000..85182db
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref
new file mode 100644
index 0000000..9ab0c74
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20202,
+ "outLabelStack":[
+ 20202
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20301,
+ "outLabelStack":[
+ 20301
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20502,
+ "outLabelStack":[
+ 20502
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20601,
+ "outLabelStack":[
+ 20601
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21202,
+ "outLabelStack":[
+ 21202
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21301":{
+ "inLabel":21301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21301,
+ "outLabelStack":[
+ 21301
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21502,
+ "outLabelStack":[
+ 21502
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21601,
+ "outLabelStack":[
+ 21601
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref
new file mode 100644
index 0000000..2ccc4f1
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref
@@ -0,0 +1,114 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: Not found
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref
new file mode 100644
index 0000000..1b57f57
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref
@@ -0,0 +1,450 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20202,
+ "outLabelStack":[
+ 20202
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20502,
+ "outLabelStack":[
+ 20502
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20601,
+ "outLabelStack":[
+ 20601
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21202,
+ "outLabelStack":[
+ 21202
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21502,
+ "outLabelStack":[
+ 21502
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21601,
+ "outLabelStack":[
+ 21601
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref
new file mode 100644
index 0000000..85182db
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref
new file mode 100644
index 0000000..9ab0c74
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20202,
+ "outLabelStack":[
+ 20202
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20301,
+ "outLabelStack":[
+ 20301
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20502,
+ "outLabelStack":[
+ 20502
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20601,
+ "outLabelStack":[
+ 20601
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21202,
+ "outLabelStack":[
+ 21202
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21301":{
+ "inLabel":21301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21301,
+ "outLabelStack":[
+ 21301
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21502,
+ "outLabelStack":[
+ 21502
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21601,
+ "outLabelStack":[
+ 21601
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref
new file mode 100644
index 0000000..85182db
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref
new file mode 100644
index 0000000..9ab0c74
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20202,
+ "outLabelStack":[
+ 20202
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20301,
+ "outLabelStack":[
+ 20301
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20502,
+ "outLabelStack":[
+ 20502
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20601,
+ "outLabelStack":[
+ 20601
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21202,
+ "outLabelStack":[
+ 21202
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21301":{
+ "inLabel":21301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21301,
+ "outLabelStack":[
+ 21301
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21502,
+ "outLabelStack":[
+ 21502
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21601,
+ "outLabelStack":[
+ 21601
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref
new file mode 100644
index 0000000..903c0f2
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref
@@ -0,0 +1,111 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: None
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref
new file mode 100644
index 0000000..1b57f57
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref
@@ -0,0 +1,450 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20202,
+ "outLabelStack":[
+ 20202
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20502,
+ "outLabelStack":[
+ 20502
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20601,
+ "outLabelStack":[
+ 20601
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21202,
+ "outLabelStack":[
+ 21202
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21502,
+ "outLabelStack":[
+ 21502
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21601,
+ "outLabelStack":[
+ 21601
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref
new file mode 100644
index 0000000..f36d965
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref
@@ -0,0 +1,107 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref
new file mode 100644
index 0000000..1b57f57
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref
@@ -0,0 +1,450 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20202,
+ "outLabelStack":[
+ 20202
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20502,
+ "outLabelStack":[
+ 20502
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20601,
+ "outLabelStack":[
+ 20601
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21202,
+ "outLabelStack":[
+ 21202
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21502,
+ "outLabelStack":[
+ 21502
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21601,
+ "outLabelStack":[
+ 21601
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref
new file mode 100644
index 0000000..85182db
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref
new file mode 100644
index 0000000..9ab0c74
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref
@@ -0,0 +1,514 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20202,
+ "outLabelStack":[
+ 20202
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20301,
+ "outLabelStack":[
+ 20301
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20502,
+ "outLabelStack":[
+ 20502
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20601,
+ "outLabelStack":[
+ 20601
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21202,
+ "outLabelStack":[
+ 21202
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21301":{
+ "inLabel":21301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21301,
+ "outLabelStack":[
+ 21301
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21502,
+ "outLabelStack":[
+ 21502
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21601,
+ "outLabelStack":[
+ 21601
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref
new file mode 100644
index 0000000..85182db
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000001
+ Bit positions: 0
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000002
+ Bit positions: 1
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: 0x00000004
+ Bit positions: 2
+ Include-all admin-group: not-set
+ Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000006
+ Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000005
+ Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: not-set
+ Include-any admin-group: 0x00000003
+ Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+ Source: 0000.0000.0002
+ Priority: 128
+ Equal to local: no
+ Local state: enabled
+ Calculation type: spf
+ Metric type: igp
+ Prefix-metric: disabled
+ Exclude SRLG: disabled
+ Exclude-any admin-group: not-set
+ Include-all admin-group: 0x00000018
+ Bit positions: 3, 4
+ Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref
new file mode 100644
index 0000000..8ae983a
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref
@@ -0,0 +1,482 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20201":{
+ "inLabel":20201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20202":{
+ "inLabel":20202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20202,
+ "outLabelStack":[
+ 20202
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20401":{
+ "inLabel":20401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20402":{
+ "inLabel":20402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20501":{
+ "inLabel":20501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20502":{
+ "inLabel":20502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20502,
+ "outLabelStack":[
+ 20502
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20601":{
+ "inLabel":20601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20601,
+ "outLabelStack":[
+ 20601
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20602":{
+ "inLabel":20602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20701":{
+ "inLabel":20701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.13.0.1"
+ }
+ ]
+ },
+ "20702":{
+ "inLabel":20702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "21001":{
+ "inLabel":21001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21002":{
+ "inLabel":21002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21101":{
+ "inLabel":21101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21102":{
+ "inLabel":21102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21201":{
+ "inLabel":21201,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21202":{
+ "inLabel":21202,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21202,
+ "outLabelStack":[
+ 21202
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21302":{
+ "inLabel":21302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21401":{
+ "inLabel":21401,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21402":{
+ "inLabel":21402,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21501":{
+ "inLabel":21501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21502":{
+ "inLabel":21502,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21502,
+ "outLabelStack":[
+ 21502
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21601":{
+ "inLabel":21601,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":21601,
+ "outLabelStack":[
+ 21601
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21602":{
+ "inLabel":21602,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ },
+ "21701":{
+ "inLabel":21701,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt1"
+ }
+ ]
+ },
+ "21702":{
+ "inLabel":21702,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "interface":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..fb45ee1
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf
@@ -0,0 +1,39 @@
+log file zebra.log
+!
+hostname rt3
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map red bit-position 0
+affinity-map blue bit-position 1
+affinity-map green bit-position 2
+affinity-map yellow bit-position 3
+affinity-map orange bit-position 4
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address 2001:db8:1000::3/128
+!
+interface eth-rt1
+ ip address 10.13.0.3/24
+ link-params
+ affinity green yellow orange
+ exit-link-params
+!
+interface eth-rt2
+ ip address 10.23.0.3/24
+ link-params
+ affinity blue yellow orange
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py b/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py
new file mode 100755
index 0000000..c81f639
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py
@@ -0,0 +1,631 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright 2021 by LINE Corporation, Hiroki Shirokura <hiroki.shirokura@linecorp.com>
+# Copyright 2023 6WIND S.A.
+
+"""
+test_isis_sr_flex_algo_topo1.py:
+
+[+] Flex-Algos 201 exclude red
+[+] Flex-Algos 202 exclude blue
+[+] Flex-Algos 203 exclude green
+[+] Flex-Algos 204 include-any blue green
+[+] Flex-Algos 205 include-any red green
+[+] Flex-Algos 206 include-any red blue
+[+] Flex-Algos 207 include-all yellow orange
+
+ +--------+ 10.12.0.0/24 +--------+
+ | | red | |
+ | RT1 |----------------| RT2 |
+ | | | |
+ +--------+ +--------+
+ 10.13.0.0/24 \\ / 10.23.0.0/24
+ green \\ / blue
+ yellow \\ / yellow
+ orange +--------+ orange
+ | |
+ | RT3 |
+ | |
+ +--------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import tempfile
+from copy import deepcopy
+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.isisd]
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+def build_topo(tgen):
+ "Build function"
+
+ def connect_routers(tgen, left_idx, right_idx):
+ left = "rt{}".format(left_idx)
+ right = "rt{}".format(right_idx)
+ switch = tgen.add_switch("s-{}-{}".format(left, right))
+ switch.add_link(tgen.gears[left], nodeif="eth-{}".format(right))
+ switch.add_link(tgen.gears[right], nodeif="eth-{}".format(left))
+ l_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, left_idx)
+ tgen.gears[left].run("ip link set eth-{} down".format(right))
+ tgen.gears[left].run("ip link set eth-{} address {}".format(right, l_addr))
+ tgen.gears[left].run("ip link set eth-{} up".format(right))
+ r_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, right_idx)
+ tgen.gears[right].run("ip link set eth-{} down".format(left))
+ tgen.gears[right].run("ip link set eth-{} address {}".format(left, r_addr))
+ tgen.gears[right].run("ip link set eth-{} up".format(left))
+
+ tgen.add_router("rt1")
+ tgen.add_router("rt2")
+ tgen.add_router("rt3")
+ connect_routers(tgen, 1, 2)
+ connect_routers(tgen, 2, 3)
+ connect_routers(tgen, 3, 1)
+
+ #
+ # Populate multi-dimensional dictionary containing all expected outputs
+ #
+ number_of_steps = 11
+ filenames = [
+ "show_mpls_table.ref",
+ "show_isis_flex_algo.ref",
+ ]
+ for rname in ["rt1", "rt2", "rt3"]:
+ outputs[rname] = {}
+ for step in range(1, number_of_steps + 1):
+ outputs[rname][step] = {}
+ for filename in filenames:
+ # Get snapshots relative to the expected network convergence
+ filename_pullpath = "{}/{}/step{}/{}".format(CWD, rname, step, filename)
+ outputs[rname][step][filename] = open(filename_pullpath).read()
+
+
+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))
+ )
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def setup_testcase(msg):
+ logger.info(msg)
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ return tgen
+
+
+def router_json_cmp_exact_filter(router, cmd, expected):
+ output = router.vtysh_cmd(cmd)
+ logger.info("{}: {}\n{}".format(router.name, cmd, output))
+
+ json_output = json.loads(output)
+ router_output = deepcopy(json_output)
+
+ # filter out dynamic data from "show mpls table"
+ for label, data in json_output.items():
+ if "1500" in label:
+ # filter out SR local labels
+ router_output.pop(label)
+ continue
+ nexthops = data.get("nexthops", [])
+ for i in range(len(nexthops)):
+ if "fe80::" in nexthops[i].get("nexthop"):
+ router_output.get(label).get("nexthops")[i].pop("nexthop")
+ elif "." in nexthops[i].get("nexthop"):
+ # IPv4, just checking the nexthop
+ router_output.get(label).get("nexthops")[i].pop("interface")
+
+ return topotest.json_cmp(router_output, expected, exact=True)
+
+
+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(
+ router_json_cmp_exact_filter, 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 router_compare_output(rname, command, reference):
+ "Compare router output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(
+ topotest.router_output_cmp, tgen.gears[rname], command, reference
+ )
+ result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5)
+ assertmsg = '{} command "{}" output mismatches the expected result:\n{}'.format(
+ rname, command, diff
+ )
+ assert result, assertmsg
+
+
+#
+# Step 1
+#
+# Test initial network convergenece
+#
+# All flex-algo are defined and its fib entries are installed
+#
+def test_step1_mpls_lfib():
+ logger.info("Test (step 1)")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # For Developers
+ # tgen.mininet_cli()
+ for rname in ["rt1", "rt2", "rt3"]:
+ router_compare_output(
+ rname, "show isis flex-algo", outputs[rname][1]["show_isis_flex_algo.ref"]
+ )
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][1]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 2
+#
+# Action(s):
+# - Disable flex-algo-203 definition advertisement on rt1
+#
+# Expected change(s):
+# - Nothing
+#
+# Description:
+# No change occurs because it refers to the FAD set in rt2.
+#
+def test_step2_mpls_lfib():
+ logger.info("Test (step 2)")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ flex-algo 203
+ no advertise-definition
+ """
+ )
+
+ # For Developers
+ # tgen.mininet_cli()
+ for rname in ["rt1", "rt2", "rt3"]:
+ router_compare_output(
+ rname, "show isis flex-algo", outputs[rname][2]["show_isis_flex_algo.ref"]
+ )
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][2]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 3
+#
+# Action(s):
+# - Disable flex-algo-203 definition advertisement on rt2
+#
+# Expected change(s):
+# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203
+#
+# Description:
+# When all FADs are disappeared, all their prefix sid routes are withdrawn.
+#
+def test_step3_mpls_lfib():
+ logger.info("Test (step 3)")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["rt2"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ flex-algo 203
+ no advertise-definition
+ """
+ )
+
+ # For Developers
+ # tgen.mininet_cli()
+ for rname in ["rt1", "rt2", "rt3"]:
+ router_compare_output(
+ rname, "show isis flex-algo", outputs[rname][3]["show_isis_flex_algo.ref"]
+ )
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][3]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 4
+#
+# Action(s):
+# - Enable flex-algo-203 definition advertisement on rt2
+#
+# Expected change(s):
+# - rt1,rt2,rt3 should install all Prefix-SIDs of flex-algo-203
+#
+# Description:
+# Since the FAD is restored, the reachability to the Prefix-SID is restored.
+#
+def test_step4_mpls_lfib():
+ logger.info("Test (step 4)")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["rt2"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ flex-algo 203
+ advertise-definition
+ """
+ )
+
+ # For Developers
+ # tgen.mininet_cli()
+ for rname in ["rt1", "rt2", "rt3"]:
+ router_compare_output(
+ rname, "show isis flex-algo", outputs[rname][4]["show_isis_flex_algo.ref"]
+ )
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][4]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 5
+#
+# Action(s):
+# - Enable flex-algo-203 definition advertisement on rt1
+#
+# Expected change(s):
+# - Nothing
+#
+# Description:
+# This does not affect the FIB, since there is already a FAD for rt2.
+# However, the FAD owner will be changed from rt2 to rt1.
+#
+def test_step5_mpls_lfib():
+ logger.info("Test (step 5)")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ flex-algo 203
+ advertise-definition
+ """
+ )
+
+ # For Developers
+ # tgen.mininet_cli()
+ for rname in ["rt1", "rt2", "rt3"]:
+ router_compare_output(
+ rname, "show isis flex-algo", outputs[rname][5]["show_isis_flex_algo.ref"]
+ )
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][5]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 6
+#
+# Action(s):
+# - Disable flex-algo-203 SR-MPLS dataplane on rt1
+# - Disable flex-algo-203 SR-MPLS dataplane on rt2
+# - Disable flex-algo-203 SR-MPLS dataplane on rt3
+#
+# Expected change(s):
+# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203
+#
+# Description:
+# Clear the Flex-Algo 203 whole settings on each routers. All routes related
+# to it will be withdrawn.
+#
+def test_step6_mpls_lfib():
+ logger.info("Test (step 6)")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3"]:
+ tgen.gears[rname].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ flex-algo 203
+ no dataplane sr-mpls
+ """
+ )
+
+ # For Developers
+ # tgen.mininet_cli()
+ for rname in ["rt1", "rt2", "rt3"]:
+ router_compare_output(
+ rname, "show isis flex-algo", outputs[rname][6]["show_isis_flex_algo.ref"]
+ )
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][6]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 7
+#
+# Action(s):
+# - Disable flex-algo-203 all configuration on rt1
+# - Disable flex-algo-203 all configuration on rt2
+# - Disable flex-algo-203 all configuration on rt3
+#
+# Expected change(s):
+# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203
+#
+# Description:
+# Clear the Flex-Algo 203 whole settings on each routers. All routes related
+# to it will be withdrawn.
+#
+def test_step7_mpls_lfib():
+ logger.info("Test (step 7)")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3"]:
+ tgen.gears[rname].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ no flex-algo 203
+ """
+ )
+
+ # For Developers
+ # tgen.mininet_cli()
+ for rname in ["rt1", "rt2", "rt3"]:
+ router_compare_output(
+ rname, "show isis flex-algo", outputs[rname][7]["show_isis_flex_algo.ref"]
+ )
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][7]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 8
+#
+# Action(s):
+# - Enable flex-algo-203 all configuration on rt1
+# - Enable flex-algo-203 all configuration on rt2
+# - Enable flex-algo-203 all configuration on rt3
+#
+# Expected change(s):
+# - rt1,rt2,rt3 should install all Prefix-SIDs of flex-algo-203
+#
+# Description:
+# All configurations were backed.
+#
+def test_step8_mpls_lfib():
+ logger.info("Test (step 8)")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ flex-algo 203
+ advertise-definition
+ affinity exclude-any green
+ dataplane sr-mpls
+ """
+ )
+
+ tgen.gears["rt2"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ flex-algo 203
+ advertise-definition
+ affinity exclude-any green
+ dataplane sr-mpls
+ """
+ )
+
+ tgen.gears["rt3"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ flex-algo 203
+ dataplane sr-mpls
+ """
+ )
+
+ # For Developers
+ # tgen.mininet_cli()
+ for rname in ["rt1", "rt2", "rt3"]:
+ router_compare_output(
+ rname, "show isis flex-algo", outputs[rname][8]["show_isis_flex_algo.ref"]
+ )
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][8]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 9
+#
+# Action(s):
+# - Disable algorithm prefix-sid of algo-203 on rt1
+#
+# Expected change(s):
+# - rt1 should uninstall all Prefix-SIDs of flex-algo-203
+# - rt2 should uninstall Prefix-SIDs of rt1's flex-algo-203
+# - rt3 should uninstall Prefix-SIDs of rt1's flex-algo-203
+#
+def test_step9_mpls_lfib():
+ logger.info("Test (step 9)")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ no segment-routing prefix 1.1.1.1/32 algorithm 203 index 301
+ no segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301
+ """
+ )
+
+ # For Developers
+ # tgen.mininet_cli()
+ for rname in ["rt1", "rt2", "rt3"]:
+ router_compare_output(
+ rname, "show isis flex-algo", outputs[rname][9]["show_isis_flex_algo.ref"]
+ )
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][9]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 10
+#
+# Action(s):
+# - Enable algorithm prefix-sid of algo-203 on rt1
+#
+# Expected change(s):
+# - rt1 should install all Prefix-SIDs of flex-algo-203
+# - rt2 should install Prefix-SIDs of rt1's flex-algo-203
+# - rt3 should install Prefix-SIDs of rt1's flex-algo-203
+#
+def test_step10_mpls_lfib():
+ logger.info("Test (step 10)")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ segment-routing prefix 1.1.1.1/32 algorithm 203 index 301
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301
+ """
+ )
+
+ # For Developers
+ # tgen.mininet_cli()
+ for rname in ["rt1", "rt2", "rt3"]:
+ router_compare_output(
+ rname, "show isis flex-algo", outputs[rname][10]["show_isis_flex_algo.ref"]
+ )
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][10]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 11
+#
+# Action(s):
+# - Update algorithm prefix-sid of algo-203 on rt1 from 301 to 311
+#
+# Expected change(s):
+# - rt2 should update Prefix-SIDs of rt1's flex-algo-203 from 301 to 311
+# - rt3 should update Prefix-SIDs of rt1's flex-algo-203 from 301 to 311
+#
+def test_step11_mpls_lfib():
+ logger.info("Test (step 11)")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ segment-routing prefix 1.1.1.1/32 algorithm 203 index 311
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1311
+ """
+ )
+
+ # For Developers
+ # tgen.mininet_cli()
+ for rname in ["rt1", "rt2", "rt3"]:
+ router_compare_output(
+ rname, "show isis flex-algo", outputs[rname][11]["show_isis_flex_algo.ref"]
+ )
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][11]["show_mpls_table.ref"]
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/README.md b/tests/topotests/isis_sr_flex_algo_topo2/README.md
new file mode 100644
index 0000000..20282c4
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/README.md
@@ -0,0 +1,8 @@
+
+## test
+
+```
+fdk-enter rt9.pid iperf3 -s
+fdk-enter rt0.pid iperf3 -B 111.111.111.111 -c 222.222.222.222 -P20 -t 100000
+fdk-enter rt0.pid watch -n0.1 ip -s link show
+```
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh b/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh
new file mode 100755
index 0000000..527f05b
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+if [ $# -ne 1 ]; then
+ echo "invalid command syntax" 1>&2
+ echo "Usage: $0 <0|128|129|130>" 1>&2
+ exit 1
+fi
+
+case "$1" in
+ 0 ) echo ;;
+ 128 ) echo ;;
+ 129 ) echo ;;
+ 130 ) echo ;;
+ * ) echo "error" ; exit ;;
+esac
+
+R0=$(cat /tmp/topotests/isis_sr_flex_algo_topo2.test_isis_sr_flex_algo_topo2/rt0.pid)
+R9=$(cat /tmp/topotests/isis_sr_flex_algo_topo2.test_isis_sr_flex_algo_topo2/rt9.pid)
+
+set -x
+
+cat <<EOF | nsenter -a -t $R0 vtysh
+conf te
+segment-routing
+ traffic-eng
+ policy color 1 endpoint 9.9.9.9
+ name sid-algorithm
+ binding-sid 111
+ candidate-path preference 100 name sid-algorithm explicit segment-list sid-algorithm-$1
+ exit
+ exit
+exit
+EOF
+
+cat <<EOF | nsenter -a -t $R9 vtysh
+conf te
+segment-routing
+ traffic-eng
+ policy color 1 endpoint 10.10.10.10
+ name sid-algorithm
+ binding-sid 222
+ candidate-path preference 100 name sid-algorithm explicit segment-list sid-algorithm-$1
+ exit
+ exit
+exit
+EOF
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf
new file mode 100644
index 0000000..3915bec
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf
@@ -0,0 +1,17 @@
+!
+router bgp 1
+ bgp router-id 10.10.10.10
+ no bgp network import-check
+ neighbor 9.9.9.9 remote-as 1
+ neighbor 9.9.9.9 update-source 10.10.10.10
+ !
+ address-family ipv4 unicast
+ network 10.255.0.0/24
+ neighbor 9.9.9.9 next-hop-self
+ neighbor 9.9.9.9 route-map sr-te in
+ exit-address-family
+!
+route-map sr-te permit 10
+ set sr-te color 1
+exit
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf
new file mode 100644
index 0000000..c102b1b
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf
@@ -0,0 +1,56 @@
+password 1
+hostname rt0
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt5
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1000.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 128
+ dataplane sr-mpls
+ advertise-definition
+ !
+ flex-algo 129
+ dataplane sr-mpls
+ advertise-definition
+ !
+ flex-algo 130
+ dataplane sr-mpls
+ advertise-definition
+ affinity include-any blue
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 10.10.10.10/32 index 0
+ segment-routing prefix 10.10.10.10/32 algorithm 128 index 100
+ segment-routing prefix 10.10.10.10/32 algorithm 129 index 200
+ segment-routing prefix 10.10.10.10/32 algorithm 130 index 300
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf
new file mode 100644
index 0000000..c51b4e0
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf
@@ -0,0 +1,20 @@
+log file pathd.log
+!
+hostname rt0
+!
+segment-routing
+ traffic-eng
+ segment-list sid-algorithm-0
+ index 10 mpls label 20009
+ exit
+ segment-list sid-algorithm-128
+ index 10 mpls label 20109
+ exit
+ segment-list sid-algorithm-129
+ index 10 mpls label 20209
+ exit
+ segment-list sid-algorithm-130
+ index 10 mpls label 20309
+ exit
+ exit
+exit
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json
new file mode 100644
index 0000000..51a1c25
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json
@@ -0,0 +1,438 @@
+{
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20002,
+ "outLabelStack":[
+ 20002
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20003,
+ "outLabelStack":[
+ 20003
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ },
+ "20004":{
+ "inLabel":20004,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20004,
+ "outLabelStack":[
+ 20004
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ },
+ "20005":{
+ "inLabel":20005,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ }
+ ]
+ },
+ "20006":{
+ "inLabel":20006,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20006,
+ "outLabelStack":[
+ 20006
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ }
+ ]
+ },
+ "20007":{
+ "inLabel":20007,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20007,
+ "outLabelStack":[
+ 20007
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ }
+ ]
+ },
+ "20008":{
+ "inLabel":20008,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20008,
+ "outLabelStack":[
+ 20008
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ }
+ ]
+ },
+ "20009":{
+ "inLabel":20009,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20009,
+ "outLabelStack":[
+ 20009
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20009,
+ "outLabelStack":[
+ 20009
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20103,
+ "outLabelStack":[
+ 20103
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ },
+ "20104":{
+ "inLabel":20104,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20104,
+ "outLabelStack":[
+ 20104
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ },
+ "20109":{
+ "inLabel":20109,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20109,
+ "outLabelStack":[
+ 20109
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ },
+ "20205":{
+ "inLabel":20205,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ }
+ ]
+ },
+ "20206":{
+ "inLabel":20206,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20206,
+ "outLabelStack":[
+ 20206
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ }
+ ]
+ },
+ "20207":{
+ "inLabel":20207,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20207,
+ "outLabelStack":[
+ 20207
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ }
+ ]
+ },
+ "20208":{
+ "inLabel":20208,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20208,
+ "outLabelStack":[
+ 20208
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ }
+ ]
+ },
+ "20209":{
+ "inLabel":20209,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20209,
+ "outLabelStack":[
+ 20209
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20302,
+ "outLabelStack":[
+ 20302
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ },
+ "20305":{
+ "inLabel":20305,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ }
+ ]
+ },
+ "20306":{
+ "inLabel":20306,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20306,
+ "outLabelStack":[
+ 20306
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ }
+ ]
+ },
+ "20307":{
+ "inLabel":20307,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20307,
+ "outLabelStack":[
+ 20307
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ }
+ ]
+ },
+ "20309":{
+ "inLabel":20309,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20309,
+ "outLabelStack":[
+ 20309
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20309,
+ "outLabelStack":[
+ 20309
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf
new file mode 100644
index 0000000..89837d4
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf
@@ -0,0 +1,34 @@
+log file zebra.log
+!
+hostname rt0
+!
+!log stdout notifications
+!log monitor notifications
+!log commands
+!
+debug zebra packet
+debug zebra dplane
+debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 10.10.10.10/32
+!
+interface eth-rt1
+ ip address 10.1.0.10/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+interface eth-rt5
+ ip address 10.5.0.10/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf
new file mode 100644
index 0000000..a793388
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf
@@ -0,0 +1,60 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt0
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt2
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt4
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt5
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1001.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 128
+ dataplane sr-mpls
+ flex-algo 130
+ dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.1/32 index 1
+ segment-routing prefix 1.1.1.1/32 algorithm 128 index 101
+ segment-routing prefix 1.1.1.1/32 algorithm 129 index 201
+ segment-routing prefix 1.1.1.1/32 algorithm 130 index 301
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json
new file mode 100644
index 0000000..5006625
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json
@@ -0,0 +1,428 @@
+{
+ "20000":{
+ "inLabel":20000,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.10"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20003,
+ "outLabelStack":[
+ 20003
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20003,
+ "outLabelStack":[
+ 20003
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20004":{
+ "inLabel":20004,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.4"
+ }
+ ]
+ },
+ "20005":{
+ "inLabel":20005,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.15.0.5"
+ }
+ ]
+ },
+ "20006":{
+ "inLabel":20006,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20006,
+ "outLabelStack":[
+ 20006
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.15.0.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20006,
+ "outLabelStack":[
+ 20006
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20007":{
+ "inLabel":20007,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20007,
+ "outLabelStack":[
+ 20007
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.15.0.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20007,
+ "outLabelStack":[
+ 20007
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20007,
+ "outLabelStack":[
+ 20007
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20008":{
+ "inLabel":20008,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20008,
+ "outLabelStack":[
+ 20008
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.15.0.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20008,
+ "outLabelStack":[
+ 20008
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.4"
+ }
+ ]
+ },
+ "20009":{
+ "inLabel":20009,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20009,
+ "outLabelStack":[
+ 20009
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20009,
+ "outLabelStack":[
+ 20009
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20100":{
+ "inLabel":20100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.10"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20103,
+ "outLabelStack":[
+ 20103
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20103,
+ "outLabelStack":[
+ 20103
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20104":{
+ "inLabel":20104,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.4"
+ }
+ ]
+ },
+ "20109":{
+ "inLabel":20109,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20109,
+ "outLabelStack":[
+ 20109
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20109,
+ "outLabelStack":[
+ 20109
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20300":{
+ "inLabel":20300,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.10"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ },
+ "20305":{
+ "inLabel":20305,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20305,
+ "outLabelStack":[
+ 20305
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.10"
+ }
+ ]
+ },
+ "20306":{
+ "inLabel":20306,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20306,
+ "outLabelStack":[
+ 20306
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.10"
+ }
+ ]
+ },
+ "20307":{
+ "inLabel":20307,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20307,
+ "outLabelStack":[
+ 20307
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20307,
+ "outLabelStack":[
+ 20307
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.1.0.10"
+ }
+ ]
+ },
+ "20309":{
+ "inLabel":20309,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20309,
+ "outLabelStack":[
+ 20309
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf
new file mode 100644
index 0000000..25a9629
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf
@@ -0,0 +1,40 @@
+log file zebra.log
+!
+hostname rt1
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface eth-rt0
+ ip address 10.1.0.1/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+interface eth-rt2
+ ip address 10.12.0.1/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+interface eth-rt4
+ ip address 10.14.0.1/24
+!
+interface eth-rt5
+ ip address 10.15.0.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf
new file mode 100644
index 0000000..312a6df
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf
@@ -0,0 +1,54 @@
+password 1
+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
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt3
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt6
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1002.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 128
+ dataplane sr-mpls
+ flex-algo 130
+ dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 2.2.2.2/32 index 2
+ segment-routing prefix 2.2.2.2/32 algorithm 128 index 102
+ segment-routing prefix 2.2.2.2/32 algorithm 129 index 202
+ segment-routing prefix 2.2.2.2/32 algorithm 130 index 302
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json
new file mode 100644
index 0000000..679b41d
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json
@@ -0,0 +1,408 @@
+{
+ "20000":{
+ "inLabel":20000,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20000,
+ "outLabelStack":[
+ 20000
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20004":{
+ "inLabel":20004,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20004,
+ "outLabelStack":[
+ 20004
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20004,
+ "outLabelStack":[
+ 20004
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20005":{
+ "inLabel":20005,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20005,
+ "outLabelStack":[
+ 20005
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.26.0.6"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20005,
+ "outLabelStack":[
+ 20005
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20006":{
+ "inLabel":20006,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.26.0.6"
+ }
+ ]
+ },
+ "20007":{
+ "inLabel":20007,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20007,
+ "outLabelStack":[
+ 20007
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.26.0.6"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20007,
+ "outLabelStack":[
+ 20007
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20008":{
+ "inLabel":20008,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20008,
+ "outLabelStack":[
+ 20008
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.26.0.6"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20008,
+ "outLabelStack":[
+ 20008
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20008,
+ "outLabelStack":[
+ 20008
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20009":{
+ "inLabel":20009,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20009,
+ "outLabelStack":[
+ 20009
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20100":{
+ "inLabel":20100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20100,
+ "outLabelStack":[
+ 20100
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20104":{
+ "inLabel":20104,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20104,
+ "outLabelStack":[
+ 20104
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20104,
+ "outLabelStack":[
+ 20104
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20109":{
+ "inLabel":20109,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20109,
+ "outLabelStack":[
+ 20109
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20300":{
+ "inLabel":20300,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20300,
+ "outLabelStack":[
+ 20300
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20305":{
+ "inLabel":20305,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20305,
+ "outLabelStack":[
+ 20305
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20306":{
+ "inLabel":20306,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20306,
+ "outLabelStack":[
+ 20306
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20306,
+ "outLabelStack":[
+ 20306
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.12.0.1"
+ }
+ ]
+ },
+ "20307":{
+ "inLabel":20307,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20307,
+ "outLabelStack":[
+ 20307
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ },
+ "20309":{
+ "inLabel":20309,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20309,
+ "outLabelStack":[
+ 20309
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf
new file mode 100644
index 0000000..d739a73
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf
@@ -0,0 +1,37 @@
+log file zebra.log
+!
+hostname rt2
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface eth-rt1
+ ip address 10.12.0.2/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+interface eth-rt3
+ ip address 10.23.0.2/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+interface eth-rt6
+ ip address 10.26.0.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf
new file mode 100644
index 0000000..c287e8a
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf
@@ -0,0 +1,60 @@
+password 1
+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
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt4
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt7
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt9
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1003.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 128
+ dataplane sr-mpls
+ flex-algo 130
+ dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 3.3.3.3/32 index 3
+ segment-routing prefix 3.3.3.3/32 algorithm 128 index 103
+ segment-routing prefix 3.3.3.3/32 algorithm 129 index 203
+ segment-routing prefix 3.3.3.3/32 algorithm 130 index 303
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json
new file mode 100644
index 0000000..f930faa
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json
@@ -0,0 +1,428 @@
+{
+ "20000":{
+ "inLabel":20000,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20000,
+ "outLabelStack":[
+ 20000
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20000,
+ "outLabelStack":[
+ 20000
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20001,
+ "outLabelStack":[
+ 20001
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20001,
+ "outLabelStack":[
+ 20001
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20004":{
+ "inLabel":20004,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.4"
+ }
+ ]
+ },
+ "20005":{
+ "inLabel":20005,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20005,
+ "outLabelStack":[
+ 20005
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.37.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20005,
+ "outLabelStack":[
+ 20005
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20005,
+ "outLabelStack":[
+ 20005
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20006":{
+ "inLabel":20006,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20006,
+ "outLabelStack":[
+ 20006
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.37.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20006,
+ "outLabelStack":[
+ 20006
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20007":{
+ "inLabel":20007,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.37.0.7"
+ }
+ ]
+ },
+ "20008":{
+ "inLabel":20008,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20008,
+ "outLabelStack":[
+ 20008
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.37.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20008,
+ "outLabelStack":[
+ 20008
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.4"
+ }
+ ]
+ },
+ "20009":{
+ "inLabel":20009,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.9"
+ }
+ ]
+ },
+ "20100":{
+ "inLabel":20100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20100,
+ "outLabelStack":[
+ 20100
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20100,
+ "outLabelStack":[
+ 20100
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20104":{
+ "inLabel":20104,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.4"
+ }
+ ]
+ },
+ "20109":{
+ "inLabel":20109,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.9"
+ }
+ ]
+ },
+ "20300":{
+ "inLabel":20300,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20300,
+ "outLabelStack":[
+ 20300
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20301,
+ "outLabelStack":[
+ 20301
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20305":{
+ "inLabel":20305,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20305,
+ "outLabelStack":[
+ 20305
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.9"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20305,
+ "outLabelStack":[
+ 20305
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.23.0.2"
+ }
+ ]
+ },
+ "20306":{
+ "inLabel":20306,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20306,
+ "outLabelStack":[
+ 20306
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.9"
+ }
+ ]
+ },
+ "20307":{
+ "inLabel":20307,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20307,
+ "outLabelStack":[
+ 20307
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.9"
+ }
+ ]
+ },
+ "20309":{
+ "inLabel":20309,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.9"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf
new file mode 100644
index 0000000..5c3bed0
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf
@@ -0,0 +1,40 @@
+log file zebra.log
+!
+hostname rt3
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface eth-rt2
+ ip address 10.23.0.3/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+interface eth-rt4
+ ip address 10.34.0.3/24
+!
+interface eth-rt7
+ ip address 10.37.0.3/24
+!
+interface eth-rt9
+ ip address 10.39.0.3/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf
new file mode 100644
index 0000000..c040ce2
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf
@@ -0,0 +1,52 @@
+password 1
+hostname rt4
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt3
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt8
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1004.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 128
+ dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 4.4.4.4/32 index 4
+ segment-routing prefix 4.4.4.4/32 algorithm 128 index 104
+ segment-routing prefix 4.4.4.4/32 algorithm 129 index 204
+ segment-routing prefix 4.4.4.4/32 algorithm 130 index 304
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json
new file mode 100644
index 0000000..141e40d
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json
@@ -0,0 +1,286 @@
+{
+ "20000":{
+ "inLabel":20000,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20000,
+ "outLabelStack":[
+ 20000
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.1"
+ }
+ ]
+ },
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20002,
+ "outLabelStack":[
+ 20002
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20002,
+ "outLabelStack":[
+ 20002
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.3"
+ }
+ ]
+ },
+ "20005":{
+ "inLabel":20005,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20005,
+ "outLabelStack":[
+ 20005
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.48.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20005,
+ "outLabelStack":[
+ 20005
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.1"
+ }
+ ]
+ },
+ "20006":{
+ "inLabel":20006,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20006,
+ "outLabelStack":[
+ 20006
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.48.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20006,
+ "outLabelStack":[
+ 20006
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20006,
+ "outLabelStack":[
+ 20006
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.1"
+ }
+ ]
+ },
+ "20007":{
+ "inLabel":20007,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20007,
+ "outLabelStack":[
+ 20007
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.48.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20007,
+ "outLabelStack":[
+ 20007
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.3"
+ }
+ ]
+ },
+ "20008":{
+ "inLabel":20008,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.48.0.8"
+ }
+ ]
+ },
+ "20009":{
+ "inLabel":20009,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20009,
+ "outLabelStack":[
+ 20009
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.3"
+ }
+ ]
+ },
+ "20100":{
+ "inLabel":20100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20100,
+ "outLabelStack":[
+ 20100
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.1"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.1"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.14.0.1"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.3"
+ }
+ ]
+ },
+ "20109":{
+ "inLabel":20109,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20109,
+ "outLabelStack":[
+ 20109
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.34.0.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf
new file mode 100644
index 0000000..9c00013
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf
@@ -0,0 +1,31 @@
+log file zebra.log
+!
+hostname rt4
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface eth-rt1
+ ip address 10.14.0.4/24
+!
+interface eth-rt3
+ ip address 10.34.0.4/24
+!
+interface eth-rt8
+ ip address 10.48.0.4/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf
new file mode 100644
index 0000000..6cf87d4
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf
@@ -0,0 +1,60 @@
+password 1
+hostname rt5
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt0
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt1
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt6
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt8
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1005.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 129
+ dataplane sr-mpls
+ flex-algo 130
+ dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 5.5.5.5/32 index 5
+ segment-routing prefix 5.5.5.5/32 algorithm 128 index 105
+ segment-routing prefix 5.5.5.5/32 algorithm 129 index 205
+ segment-routing prefix 5.5.5.5/32 algorithm 130 index 305
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json
new file mode 100644
index 0000000..82ebfc0
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json
@@ -0,0 +1,428 @@
+{
+ "20000":{
+ "inLabel":20000,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.10"
+ }
+ ]
+ },
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.15.0.1"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20002,
+ "outLabelStack":[
+ 20002
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.6"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20002,
+ "outLabelStack":[
+ 20002
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.15.0.1"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20003,
+ "outLabelStack":[
+ 20003
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20003,
+ "outLabelStack":[
+ 20003
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.6"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20003,
+ "outLabelStack":[
+ 20003
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.15.0.1"
+ }
+ ]
+ },
+ "20004":{
+ "inLabel":20004,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20004,
+ "outLabelStack":[
+ 20004
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20004,
+ "outLabelStack":[
+ 20004
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.15.0.1"
+ }
+ ]
+ },
+ "20006":{
+ "inLabel":20006,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.6"
+ }
+ ]
+ },
+ "20007":{
+ "inLabel":20007,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20007,
+ "outLabelStack":[
+ 20007
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20007,
+ "outLabelStack":[
+ 20007
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.6"
+ }
+ ]
+ },
+ "20008":{
+ "inLabel":20008,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.8"
+ }
+ ]
+ },
+ "20009":{
+ "inLabel":20009,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20009,
+ "outLabelStack":[
+ 20009
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20009,
+ "outLabelStack":[
+ 20009
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.6"
+ }
+ ]
+ },
+ "20200":{
+ "inLabel":20200,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.10"
+ }
+ ]
+ },
+ "20206":{
+ "inLabel":20206,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.6"
+ }
+ ]
+ },
+ "20207":{
+ "inLabel":20207,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20207,
+ "outLabelStack":[
+ 20207
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20207,
+ "outLabelStack":[
+ 20207
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.6"
+ }
+ ]
+ },
+ "20208":{
+ "inLabel":20208,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.8"
+ }
+ ]
+ },
+ "20209":{
+ "inLabel":20209,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20209,
+ "outLabelStack":[
+ 20209
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20209,
+ "outLabelStack":[
+ 20209
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.6"
+ }
+ ]
+ },
+ "20300":{
+ "inLabel":20300,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.10"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20301,
+ "outLabelStack":[
+ 20301
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.10"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20302,
+ "outLabelStack":[
+ 20302
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.10"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.6"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.5.0.10"
+ }
+ ]
+ },
+ "20306":{
+ "inLabel":20306,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.6"
+ }
+ ]
+ },
+ "20307":{
+ "inLabel":20307,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20307,
+ "outLabelStack":[
+ 20307
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.6"
+ }
+ ]
+ },
+ "20309":{
+ "inLabel":20309,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20309,
+ "outLabelStack":[
+ 20309
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf
new file mode 100644
index 0000000..61c599d
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf
@@ -0,0 +1,40 @@
+log file zebra.log
+!
+hostname rt5
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 5.5.5.5/32
+!
+interface eth-rt0
+ ip address 10.5.0.5/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+interface eth-rt1
+ ip address 10.15.0.5/24
+!
+interface eth-rt6
+ ip address 10.56.0.5/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+interface eth-rt8
+ ip address 10.58.0.5/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf
new file mode 100644
index 0000000..87a2596
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf
@@ -0,0 +1,54 @@
+password 1
+hostname rt6
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt5
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt7
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1006.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 129
+ dataplane sr-mpls
+ flex-algo 130
+ dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 6.6.6.6/32 index 6
+ segment-routing prefix 6.6.6.6/32 algorithm 128 index 106
+ segment-routing prefix 6.6.6.6/32 algorithm 129 index 206
+ segment-routing prefix 6.6.6.6/32 algorithm 130 index 306
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json
new file mode 100644
index 0000000..2cc7277
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json
@@ -0,0 +1,408 @@
+{
+ "20000":{
+ "inLabel":20000,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20000,
+ "outLabelStack":[
+ 20000
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.5"
+ }
+ ]
+ },
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20001,
+ "outLabelStack":[
+ 20001
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20001,
+ "outLabelStack":[
+ 20001
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.26.0.2"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.26.0.2"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20003,
+ "outLabelStack":[
+ 20003
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20003,
+ "outLabelStack":[
+ 20003
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.26.0.2"
+ }
+ ]
+ },
+ "20004":{
+ "inLabel":20004,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20004,
+ "outLabelStack":[
+ 20004
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20004,
+ "outLabelStack":[
+ 20004
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20004,
+ "outLabelStack":[
+ 20004
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.26.0.2"
+ }
+ ]
+ },
+ "20005":{
+ "inLabel":20005,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.5"
+ }
+ ]
+ },
+ "20007":{
+ "inLabel":20007,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.7"
+ }
+ ]
+ },
+ "20008":{
+ "inLabel":20008,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20008,
+ "outLabelStack":[
+ 20008
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20008,
+ "outLabelStack":[
+ 20008
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.5"
+ }
+ ]
+ },
+ "20009":{
+ "inLabel":20009,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20009,
+ "outLabelStack":[
+ 20009
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.7"
+ }
+ ]
+ },
+ "20200":{
+ "inLabel":20200,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20200,
+ "outLabelStack":[
+ 20200
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.5"
+ }
+ ]
+ },
+ "20205":{
+ "inLabel":20205,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.5"
+ }
+ ]
+ },
+ "20207":{
+ "inLabel":20207,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.7"
+ }
+ ]
+ },
+ "20208":{
+ "inLabel":20208,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20208,
+ "outLabelStack":[
+ 20208
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20208,
+ "outLabelStack":[
+ 20208
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.5"
+ }
+ ]
+ },
+ "20209":{
+ "inLabel":20209,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20209,
+ "outLabelStack":[
+ 20209
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.7"
+ }
+ ]
+ },
+ "20300":{
+ "inLabel":20300,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20300,
+ "outLabelStack":[
+ 20300
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.5"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20301,
+ "outLabelStack":[
+ 20301
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.5"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20302,
+ "outLabelStack":[
+ 20302
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20302,
+ "outLabelStack":[
+ 20302
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.5"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.7"
+ }
+ ]
+ },
+ "20305":{
+ "inLabel":20305,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.56.0.5"
+ }
+ ]
+ },
+ "20307":{
+ "inLabel":20307,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.7"
+ }
+ ]
+ },
+ "20309":{
+ "inLabel":20309,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20309,
+ "outLabelStack":[
+ 20309
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.7"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf
new file mode 100644
index 0000000..b63401e
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf
@@ -0,0 +1,37 @@
+log file zebra.log
+!
+hostname rt6
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 6.6.6.6/32
+!
+interface eth-rt2
+ ip address 10.26.0.6/24
+!
+interface eth-rt5
+ ip address 10.56.0.6/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+interface eth-rt7
+ ip address 10.67.0.6/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf
new file mode 100644
index 0000000..6645542
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf
@@ -0,0 +1,60 @@
+password 1
+hostname rt7
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt3
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt6
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt8
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt9
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1007.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 129
+ dataplane sr-mpls
+ flex-algo 130
+ dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 7.7.7.7/32 index 7
+ segment-routing prefix 7.7.7.7/32 algorithm 128 index 107
+ segment-routing prefix 7.7.7.7/32 algorithm 129 index 207
+ segment-routing prefix 7.7.7.7/32 algorithm 130 index 307
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json
new file mode 100644
index 0000000..aeaa604
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json
@@ -0,0 +1,428 @@
+{
+ "20000":{
+ "inLabel":20000,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20000,
+ "outLabelStack":[
+ 20000
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20000,
+ "outLabelStack":[
+ 20000
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.6"
+ }
+ ]
+ },
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20001,
+ "outLabelStack":[
+ 20001
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20001,
+ "outLabelStack":[
+ 20001
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.6"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20001,
+ "outLabelStack":[
+ 20001
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.37.0.3"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20002,
+ "outLabelStack":[
+ 20002
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.6"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20002,
+ "outLabelStack":[
+ 20002
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.37.0.3"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.37.0.3"
+ }
+ ]
+ },
+ "20004":{
+ "inLabel":20004,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20004,
+ "outLabelStack":[
+ 20004
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20004,
+ "outLabelStack":[
+ 20004
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.37.0.3"
+ }
+ ]
+ },
+ "20005":{
+ "inLabel":20005,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20005,
+ "outLabelStack":[
+ 20005
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20005,
+ "outLabelStack":[
+ 20005
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.6"
+ }
+ ]
+ },
+ "20006":{
+ "inLabel":20006,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.6"
+ }
+ ]
+ },
+ "20008":{
+ "inLabel":20008,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.8"
+ }
+ ]
+ },
+ "20009":{
+ "inLabel":20009,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.9"
+ }
+ ]
+ },
+ "20200":{
+ "inLabel":20200,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20200,
+ "outLabelStack":[
+ 20200
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20200,
+ "outLabelStack":[
+ 20200
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.6"
+ }
+ ]
+ },
+ "20205":{
+ "inLabel":20205,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20205,
+ "outLabelStack":[
+ 20205
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.8"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20205,
+ "outLabelStack":[
+ 20205
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.6"
+ }
+ ]
+ },
+ "20206":{
+ "inLabel":20206,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.6"
+ }
+ ]
+ },
+ "20208":{
+ "inLabel":20208,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.8"
+ }
+ ]
+ },
+ "20209":{
+ "inLabel":20209,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.9"
+ }
+ ]
+ },
+ "20300":{
+ "inLabel":20300,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20300,
+ "outLabelStack":[
+ 20300
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.6"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20301,
+ "outLabelStack":[
+ 20301
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.9"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20301,
+ "outLabelStack":[
+ 20301
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.6"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20302,
+ "outLabelStack":[
+ 20302
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.9"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20303,
+ "outLabelStack":[
+ 20303
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.9"
+ }
+ ]
+ },
+ "20305":{
+ "inLabel":20305,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20305,
+ "outLabelStack":[
+ 20305
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.6"
+ }
+ ]
+ },
+ "20306":{
+ "inLabel":20306,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.67.0.6"
+ }
+ ]
+ },
+ "20309":{
+ "inLabel":20309,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.9"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf
new file mode 100644
index 0000000..b5a28c7
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf
@@ -0,0 +1,40 @@
+log file zebra.log
+!
+hostname rt7
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 7.7.7.7/32
+!
+interface eth-rt3
+ ip address 10.37.0.7/24
+!
+interface eth-rt6
+ ip address 10.67.0.7/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+interface eth-rt8
+ ip address 10.78.0.7/24
+!
+interface eth-rt9
+ ip address 10.79.0.7/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf
new file mode 100644
index 0000000..04e925e
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf
@@ -0,0 +1,50 @@
+password 1
+hostname rt8
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt4
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt5
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt7
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1008.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 129
+ dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 8.8.8.8/32 index 8
+ segment-routing prefix 8.8.8.8/32 algorithm 128 index 108
+ segment-routing prefix 8.8.8.8/32 algorithm 129 index 208
+ segment-routing prefix 8.8.8.8/32 algorithm 130 index 308
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json
new file mode 100644
index 0000000..27470b7
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json
@@ -0,0 +1,286 @@
+{
+ "20000":{
+ "inLabel":20000,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20000,
+ "outLabelStack":[
+ 20000
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.5"
+ }
+ ]
+ },
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20001,
+ "outLabelStack":[
+ 20001
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20001,
+ "outLabelStack":[
+ 20001
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.48.0.4"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20002,
+ "outLabelStack":[
+ 20002
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20002,
+ "outLabelStack":[
+ 20002
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20002,
+ "outLabelStack":[
+ 20002
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.48.0.4"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20003,
+ "outLabelStack":[
+ 20003
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20003,
+ "outLabelStack":[
+ 20003
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.48.0.4"
+ }
+ ]
+ },
+ "20004":{
+ "inLabel":20004,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.48.0.4"
+ }
+ ]
+ },
+ "20005":{
+ "inLabel":20005,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.5"
+ }
+ ]
+ },
+ "20006":{
+ "inLabel":20006,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20006,
+ "outLabelStack":[
+ 20006
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20006,
+ "outLabelStack":[
+ 20006
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.5"
+ }
+ ]
+ },
+ "20007":{
+ "inLabel":20007,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.7"
+ }
+ ]
+ },
+ "20009":{
+ "inLabel":20009,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20009,
+ "outLabelStack":[
+ 20009
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.7"
+ }
+ ]
+ },
+ "20200":{
+ "inLabel":20200,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20200,
+ "outLabelStack":[
+ 20200
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.5"
+ }
+ ]
+ },
+ "20205":{
+ "inLabel":20205,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.5"
+ }
+ ]
+ },
+ "20206":{
+ "inLabel":20206,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20206,
+ "outLabelStack":[
+ 20206
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20206,
+ "outLabelStack":[
+ 20206
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.58.0.5"
+ }
+ ]
+ },
+ "20207":{
+ "inLabel":20207,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.7"
+ }
+ ]
+ },
+ "20209":{
+ "inLabel":20209,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20209,
+ "outLabelStack":[
+ 20209
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.78.0.7"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf
new file mode 100644
index 0000000..dd63f8c
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname rt8
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+interface lo
+ ip address 8.8.8.8/32
+!
+interface eth-rt4
+ ip address 10.48.0.8/24
+!
+interface eth-rt5
+ ip address 10.58.0.8/24
+!
+interface eth-rt7
+ ip address 10.78.0.8/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf
new file mode 100644
index 0000000..386d811
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf
@@ -0,0 +1,17 @@
+!
+router bgp 1
+ bgp router-id 9.9.9.9
+ no bgp network import-check
+ neighbor 10.10.10.10 remote-as 1
+ neighbor 10.10.10.10 update-source 9.9.9.9
+ !
+ address-family ipv4 unicast
+ network 10.255.9.0/24
+ neighbor 10.10.10.10 next-hop-self
+ neighbor 10.10.10.10 route-map sr-te in
+ exit-address-family
+!
+route-map sr-te permit 10
+ set sr-te color 1
+exit
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf
new file mode 100644
index 0000000..dabb998
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf
@@ -0,0 +1,56 @@
+password 1
+hostname rt9
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt3
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt7
+ ip router isis 1
+ isis hello-multiplier 10
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1009.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 128
+ dataplane sr-mpls
+ advertise-definition
+ !
+ flex-algo 129
+ dataplane sr-mpls
+ advertise-definition
+ !
+ flex-algo 130
+ dataplane sr-mpls
+ advertise-definition
+ affinity include-any blue
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 9.9.9.9/32 index 9
+ segment-routing prefix 9.9.9.9/32 algorithm 128 index 109
+ segment-routing prefix 9.9.9.9/32 algorithm 129 index 209
+ segment-routing prefix 9.9.9.9/32 algorithm 130 index 309
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf
new file mode 100644
index 0000000..3f9a8d9
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf
@@ -0,0 +1,20 @@
+log file pathd.log
+!
+hostname rt9
+!
+segment-routing
+ traffic-eng
+ segment-list sid-algorithm-0
+ index 10 mpls label 20000
+ exit
+ segment-list sid-algorithm-128
+ index 10 mpls label 20100
+ exit
+ segment-list sid-algorithm-129
+ index 10 mpls label 20200
+ exit
+ segment-list sid-algorithm-130
+ index 10 mpls label 20300
+ exit
+ exit
+exit
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json
new file mode 100644
index 0000000..e98680c
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json
@@ -0,0 +1,438 @@
+{
+ "20000":{
+ "inLabel":20000,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20000,
+ "outLabelStack":[
+ 20000
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20000,
+ "outLabelStack":[
+ 20000
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20001":{
+ "inLabel":20001,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20001,
+ "outLabelStack":[
+ 20001
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20002":{
+ "inLabel":20002,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20002,
+ "outLabelStack":[
+ 20002
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20003":{
+ "inLabel":20003,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20004":{
+ "inLabel":20004,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20004,
+ "outLabelStack":[
+ 20004
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20005":{
+ "inLabel":20005,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20005,
+ "outLabelStack":[
+ 20005
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ }
+ ]
+ },
+ "20006":{
+ "inLabel":20006,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20006,
+ "outLabelStack":[
+ 20006
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ }
+ ]
+ },
+ "20007":{
+ "inLabel":20007,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ }
+ ]
+ },
+ "20008":{
+ "inLabel":20008,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20008,
+ "outLabelStack":[
+ 20008
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ }
+ ]
+ },
+ "20100":{
+ "inLabel":20100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20100,
+ "outLabelStack":[
+ 20100
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20101":{
+ "inLabel":20101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20101,
+ "outLabelStack":[
+ 20101
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20102":{
+ "inLabel":20102,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20102,
+ "outLabelStack":[
+ 20102
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20103":{
+ "inLabel":20103,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20104":{
+ "inLabel":20104,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20104,
+ "outLabelStack":[
+ 20104
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20200":{
+ "inLabel":20200,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20200,
+ "outLabelStack":[
+ 20200
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ }
+ ]
+ },
+ "20205":{
+ "inLabel":20205,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20205,
+ "outLabelStack":[
+ 20205
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ }
+ ]
+ },
+ "20206":{
+ "inLabel":20206,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20206,
+ "outLabelStack":[
+ 20206
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ }
+ ]
+ },
+ "20207":{
+ "inLabel":20207,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ }
+ ]
+ },
+ "20208":{
+ "inLabel":20208,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20208,
+ "outLabelStack":[
+ 20208
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ }
+ ]
+ },
+ "20300":{
+ "inLabel":20300,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20300,
+ "outLabelStack":[
+ 20300
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20300,
+ "outLabelStack":[
+ 20300
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20301":{
+ "inLabel":20301,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20301,
+ "outLabelStack":[
+ 20301
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20302":{
+ "inLabel":20302,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20302,
+ "outLabelStack":[
+ 20302
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20303":{
+ "inLabel":20303,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.39.0.3"
+ }
+ ]
+ },
+ "20305":{
+ "inLabel":20305,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20305,
+ "outLabelStack":[
+ 20305
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ }
+ ]
+ },
+ "20306":{
+ "inLabel":20306,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":20306,
+ "outLabelStack":[
+ 20306
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ }
+ ]
+ },
+ "20307":{
+ "inLabel":20307,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.79.0.7"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf
new file mode 100644
index 0000000..378a196
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf
@@ -0,0 +1,34 @@
+log file zebra.log
+!
+hostname rt9
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 9.9.9.9/32
+!
+interface eth-rt3
+ ip address 10.39.0.9/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+interface eth-rt7
+ ip address 10.79.0.9/24
+ link-params
+ affinity blue
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py b/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py
new file mode 100755
index 0000000..6a5f81d
--- /dev/null
+++ b/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright 2021 by LINE Corporation, Hiroki Shirokura <hiroki.shirokura@linecorp.com>
+# Copyright 2023 6WIND S.A.
+
+"""
+test_isis_sr_flex_algo_topo2.py:
+
+[+] Flex-Algos 128
+[+] Flex-Algos 129
+[+] Flex-Algos 130 include-any blue
+
+ +--------+ +--------+
+ | | | |
+ | RT1 |------------------| RT2 |
+ | | | |
+ +--------+ +--------+
+ / | \\ | \\
+ / | \\ | \\
++--------+ | \\ | \\
+| | | +--------+ | +--------+
+| RT0 | | | | | | |
+| | | | RT4 |------------------| RT3 |
++--------+ | | | | | |
+ \\ | +--------+ | +--------+
+ \\ | | | | \\
+ +--------+ | +--------+ | \\
+ | | | | | | +--------+
+ | RT5 |-------|----------| RT6 | | | |
+ | | | | | | | RT9 |
+ +--------+ | +--------+ | | |
+ \\ | \\ | +--------+
+ \\ | \\ | /
+ \\ | \\ | /
+ +--------+ +--------+
+ | | | |
+ | RT8 |------------------| RT7 |
+ | | | |
+ +--------+ +--------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import time
+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.isisd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ routers = []
+ for i in range(0, 10):
+ rt = tgen.add_router("rt{}".format(i))
+ rt.run("sysctl -w net.ipv4.fib_multipath_hash_policy=1")
+
+ def connect_routers(tgen, left_idx, right_idx):
+ left = "rt{}".format(left_idx)
+ right = "rt{}".format(right_idx)
+ switch = tgen.add_switch("s-{}-{}".format(left, right))
+ switch.add_link(tgen.gears[left], nodeif="eth-{}".format(right))
+ switch.add_link(tgen.gears[right], nodeif="eth-{}".format(left))
+ l_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, left_idx)
+ tgen.gears[left].run("ip link set eth-{} down".format(right))
+ tgen.gears[left].run("ip link set eth-{} address {}".format(right, l_addr))
+ tgen.gears[left].run("ip link set eth-{} up".format(right))
+ tgen.gears[left].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(right))
+ r_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, right_idx)
+ tgen.gears[right].run("ip link set eth-{} down".format(left))
+ tgen.gears[right].run("ip link set eth-{} address {}".format(left, r_addr))
+ tgen.gears[right].run("ip link set eth-{} up".format(left))
+ tgen.gears[right].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(left))
+
+ connect_routers(tgen, 0, 1)
+ connect_routers(tgen, 0, 5)
+ connect_routers(tgen, 1, 2)
+ connect_routers(tgen, 1, 4)
+ connect_routers(tgen, 1, 5)
+ connect_routers(tgen, 2, 3)
+ connect_routers(tgen, 2, 6)
+ connect_routers(tgen, 3, 4)
+ connect_routers(tgen, 3, 7)
+ connect_routers(tgen, 3, 9)
+ connect_routers(tgen, 4, 8)
+ connect_routers(tgen, 5, 6)
+ connect_routers(tgen, 5, 8)
+ connect_routers(tgen, 6, 7)
+ connect_routers(tgen, 7, 8)
+ connect_routers(tgen, 7, 9)
+
+
+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)))
+ if rname in ["rt0", "rt9"]:
+ router.load_config( TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)))
+ router.load_config( TopoRouter.RD_PATH, os.path.join(CWD, "{}/pathd.conf".format(rname)))
+ router.run("ip link add dum0 type dummy")
+ router.run("ip link set dum0 up")
+ if rname == "rt0":
+ router.run("ip addr add 10.255.0.1/24 dev dum0")
+ elif rname == "rt9":
+ router.run("ip addr add 10.255.9.1/24 dev dum0")
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def setup_testcase(msg):
+ logger.info(msg)
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ return tgen
+
+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_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 = partial(_check, name, cmd, expected_file)
+ success, result = topotest.run_and_expect(func, None, count=120, wait=0.5)
+ assert result is None, "Failed"
+
+
+def test_rib():
+ check_rib("rt0", "show mpls table json", "rt0/step1/route.json")
+ check_rib("rt1", "show mpls table json", "rt1/step1/route.json")
+ check_rib("rt2", "show mpls table json", "rt2/step1/route.json")
+ check_rib("rt3", "show mpls table json", "rt3/step1/route.json")
+ check_rib("rt4", "show mpls table json", "rt4/step1/route.json")
+ check_rib("rt5", "show mpls table json", "rt5/step1/route.json")
+ check_rib("rt6", "show mpls table json", "rt6/step1/route.json")
+ check_rib("rt7", "show mpls table json", "rt7/step1/route.json")
+ check_rib("rt8", "show mpls table json", "rt8/step1/route.json")
+ check_rib("rt9", "show mpls table json", "rt9/step1/route.json")
+
+
+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..3a94af7
--- /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 10
+!
+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..553e72c
--- /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 10
+!
+interface eth-rt4-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 10
+!
+interface eth-rt4-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 10
+!
+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..8d89612
--- /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 10
+!
+interface eth-rt5-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 10
+!
+interface eth-rt5-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 10
+!
+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..e5f72a7
--- /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 10
+!
+interface eth-rt2-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 10
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 10
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 10
+!
+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..1a66d18
--- /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 10
+!
+interface eth-rt3-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 10
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 10
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 10
+!
+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..bfa9883
--- /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 10
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 10
+!
+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..fc2ce7c
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/test_isis_sr_te_topo1.py
@@ -0,0 +1,857 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_isis_sr_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..ef8d427
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/isisd.conf
@@ -0,0 +1,32 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+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..9c5901b
--- /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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "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..e65ec81
--- /dev/null
+++ b/tests/topotests/isis_sr_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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt4-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt4-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..5e46ddf
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 10,
+ "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..e7b8d94
--- /dev/null
+++ b/tests/topotests/isis_sr_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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt5-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt5-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..a284240
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "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..92ea1ea
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/isisd.conf
@@ -0,0 +1,55 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt2-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..0ca7a76
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "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..52682ff
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "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..a2e920c
--- /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": 10,
+ "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": 10,
+ "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..de604d7
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/isisd.conf
@@ -0,0 +1,55 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt3-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..f40b0d3
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "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..153e866
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "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..2008d84
--- /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": 10,
+ "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": 10,
+ "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..153e866
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "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..b96a2c6
--- /dev/null
+++ b/tests/topotests/isis_sr_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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..8300ca0
--- /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": 10,
+ "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": 10,
+ "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..9a4085a
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/test_isis_sr_topo1.py
@@ -0,0 +1,1073 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_isis_sr_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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-interval 1" -c "isis hello-multiplier 10"'
+ )
+ 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_srv6_topo1/dst/sharpd.conf b/tests/topotests/isis_srv6_topo1/dst/sharpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/dst/sharpd.conf
diff --git a/tests/topotests/isis_srv6_topo1/dst/zebra.conf b/tests/topotests/isis_srv6_topo1/dst/zebra.conf
new file mode 100644
index 0000000..8074185
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/dst/zebra.conf
@@ -0,0 +1,22 @@
+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 fc00:0:9::1/128
+!
+interface eth-rt6
+ ip address 10.0.10.2/24
+ ipv6 address 2001:db8:10::2/64
+!
+ip forwarding
+!
+ip route 2001:db8:1::1 2001:db8:10::1
+!
+line vty
+!
diff --git a/tests/topotests/isis_srv6_topo1/rt1/isisd.conf b/tests/topotests/isis_srv6_topo1/rt1/isisd.conf
new file mode 100644
index 0000000..29e1a31
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/isisd.conf
@@ -0,0 +1,35 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0001.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing srv6
+ locator loc1
+ node-msd
+ max-segs-left 3
+ max-end-pop 3
+ max-h-encaps 2
+ max-end-d 5
+ interface sr0
+!
diff --git a/tests/topotests/isis_srv6_topo1/rt1/sharpd.conf b/tests/topotests/isis_srv6_topo1/rt1/sharpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/sharpd.conf
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step1/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step1/show_ip_route.ref
new file mode 100644
index 0000000..590d75a
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step1/show_ip_route.ref
@@ -0,0 +1,276 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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.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_srv6_topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..2d30fc4
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step1/show_ipv6_route.ref
@@ -0,0 +1,270 @@
+{
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:1:1::\/64":[
+ {
+ "prefix":"fc00:0:1:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:1:2::\/64":[
+ {
+ "prefix":"fc00:0:1:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step1/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt1/step1/show_srv6_locator_table.ref
new file mode 100644
index 0000000..bb10aba
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step1/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:1::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:1::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..9c5901b
--- /dev/null
+++ b/tests/topotests/isis_srv6_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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step2/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step2/show_ip_route.ref
new file mode 100644
index 0000000..590d75a
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step2/show_ip_route.ref
@@ -0,0 +1,276 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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.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_srv6_topo1/rt1/step2/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..9dda5dc
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step2/show_ipv6_route.ref
@@ -0,0 +1,204 @@
+{
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step2/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt1/step2/show_srv6_locator_table.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step2/show_srv6_locator_table.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt1/step2/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..9c5901b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step2/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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step3/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step3/show_ip_route.ref
new file mode 100644
index 0000000..590d75a
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step3/show_ip_route.ref
@@ -0,0 +1,276 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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.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_srv6_topo1/rt1/step3/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..2d30fc4
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step3/show_ipv6_route.ref
@@ -0,0 +1,270 @@
+{
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:1:1::\/64":[
+ {
+ "prefix":"fc00:0:1:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:1:2::\/64":[
+ {
+ "prefix":"fc00:0:1:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step3/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt1/step3/show_srv6_locator_table.ref
new file mode 100644
index 0000000..bb10aba
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step3/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:1::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:1::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt1/step3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..9c5901b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step3/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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step4/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step4/show_ip_route.ref
new file mode 100644
index 0000000..590d75a
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step4/show_ip_route.ref
@@ -0,0 +1,276 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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.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_srv6_topo1/rt1/step4/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..9dda5dc
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step4/show_ipv6_route.ref
@@ -0,0 +1,204 @@
+{
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step4/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt1/step4/show_srv6_locator_table.ref
new file mode 100644
index 0000000..f9561be
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step4/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:1::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:1::/48",
+ "proto":"system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt1/step4/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..9c5901b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step4/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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step5/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step5/show_ip_route.ref
new file mode 100644
index 0000000..590d75a
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step5/show_ip_route.ref
@@ -0,0 +1,276 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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.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_srv6_topo1/rt1/step5/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step5/show_ipv6_route.ref
new file mode 100644
index 0000000..2d30fc4
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step5/show_ipv6_route.ref
@@ -0,0 +1,270 @@
+{
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:1:1::\/64":[
+ {
+ "prefix":"fc00:0:1:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:1:2::\/64":[
+ {
+ "prefix":"fc00:0:1:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step5/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt1/step5/show_srv6_locator_table.ref
new file mode 100644
index 0000000..bb10aba
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step5/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:1::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:1::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt1/step5/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..9c5901b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step5/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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step6/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step6/show_ip_route.ref
new file mode 100644
index 0000000..590d75a
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step6/show_ip_route.ref
@@ -0,0 +1,276 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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.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_srv6_topo1/rt1/step6/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step6/show_ipv6_route.ref
new file mode 100644
index 0000000..9dda5dc
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step6/show_ipv6_route.ref
@@ -0,0 +1,204 @@
+{
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step6/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt1/step6/show_srv6_locator_table.ref
new file mode 100644
index 0000000..f9561be
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step6/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:1::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:1::/48",
+ "proto":"system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt1/step6/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..9c5901b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step6/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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step7/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step7/show_ip_route.ref
new file mode 100644
index 0000000..590d75a
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step7/show_ip_route.ref
@@ -0,0 +1,276 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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.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_srv6_topo1/rt1/step7/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step7/show_ipv6_route.ref
new file mode 100644
index 0000000..2d30fc4
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step7/show_ipv6_route.ref
@@ -0,0 +1,270 @@
+{
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:1:1::\/64":[
+ {
+ "prefix":"fc00:0:1:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:1:2::\/64":[
+ {
+ "prefix":"fc00:0:1:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step7/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt1/step7/show_srv6_locator_table.ref
new file mode 100644
index 0000000..bb10aba
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step7/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:1::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:1::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt1/step7/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..9c5901b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step7/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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step8/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step8/show_ip_route.ref
new file mode 100644
index 0000000..590d75a
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step8/show_ip_route.ref
@@ -0,0 +1,276 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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.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_srv6_topo1/rt1/step8/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step8/show_ipv6_route.ref
new file mode 100644
index 0000000..9dda5dc
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step8/show_ipv6_route.ref
@@ -0,0 +1,204 @@
+{
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step8/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt1/step8/show_srv6_locator_table.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step8/show_srv6_locator_table.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt1/step8/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..9c5901b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step8/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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step9/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step9/show_ip_route.ref
new file mode 100644
index 0000000..590d75a
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step9/show_ip_route.ref
@@ -0,0 +1,276 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ],
+ "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.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_srv6_topo1/rt1/step9/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt1/step9/show_ipv6_route.ref
new file mode 100644
index 0000000..2d30fc4
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step9/show_ipv6_route.ref
@@ -0,0 +1,270 @@
+{
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:1:1::\/64":[
+ {
+ "prefix":"fc00:0:1:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:1:2::\/64":[
+ {
+ "prefix":"fc00:0:1:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step9/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt1/step9/show_srv6_locator_table.ref
new file mode 100644
index 0000000..bb10aba
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step9/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:1::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:1::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt1/step9/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..9c5901b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/step9/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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt1/zebra.conf b/tests/topotests/isis_srv6_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..05f60fe
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt1/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+!
+hostname rt1
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address fc00:0:1::1/128
+!
+interface eth-sw1
+ ip address 10.0.1.1/24
+ ipv6 address 2001:db8:1::1/64
+!
+segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix fc00:0:1::/48 block-len 32 node-len 16 func-bits 16
+ behavior usid
+ !
+ !
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_srv6_topo1/rt2/isisd.conf b/tests/topotests/isis_srv6_topo1/rt2/isisd.conf
new file mode 100644
index 0000000..b095f04
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/isisd.conf
@@ -0,0 +1,48 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt4-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt4-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0002.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing srv6
+ locator loc1
+ node-msd
+ max-segs-left 3
+ max-end-pop 3
+ max-h-encaps 2
+ max-end-d 5
+ interface sr0
+!
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step1/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step1/show_ip_route.ref
new file mode 100644
index 0000000..1d4a9e9
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step1/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step1/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..150fa92
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step1/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:1::\/64":[
+ {
+ "prefix":"fc00:0:2:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:2::\/64":[
+ {
+ "prefix":"fc00:0:2:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:3::\/64":[
+ {
+ "prefix":"fc00:0:2:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:4::\/64":[
+ {
+ "prefix":"fc00:0:2:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step1/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt2/step1/show_srv6_locator_table.ref
new file mode 100644
index 0000000..f339931
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step1/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:2::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:2::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..5e46ddf
--- /dev/null
+++ b/tests/topotests/isis_srv6_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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step2/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step2/show_ip_route.ref
new file mode 100644
index 0000000..1d4a9e9
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step2/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step2/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..f9c2d09
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step2/show_ipv6_route.ref
@@ -0,0 +1,327 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:1::\/64":[
+ {
+ "prefix":"fc00:0:2:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:2::\/64":[
+ {
+ "prefix":"fc00:0:2:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:3::\/64":[
+ {
+ "prefix":"fc00:0:2:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:4::\/64":[
+ {
+ "prefix":"fc00:0:2:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step2/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt2/step2/show_srv6_locator_table.ref
new file mode 100644
index 0000000..f339931
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step2/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:2::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:2::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt2/step2/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..5e46ddf
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step2/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step3/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step3/show_ip_route.ref
new file mode 100644
index 0000000..1d4a9e9
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step3/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step3/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..150fa92
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step3/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:1::\/64":[
+ {
+ "prefix":"fc00:0:2:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:2::\/64":[
+ {
+ "prefix":"fc00:0:2:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:3::\/64":[
+ {
+ "prefix":"fc00:0:2:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:4::\/64":[
+ {
+ "prefix":"fc00:0:2:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step3/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt2/step3/show_srv6_locator_table.ref
new file mode 100644
index 0000000..f339931
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step3/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:2::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:2::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt2/step3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..5e46ddf
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step3/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step4/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step4/show_ip_route.ref
new file mode 100644
index 0000000..1d4a9e9
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step4/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step4/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..f9c2d09
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step4/show_ipv6_route.ref
@@ -0,0 +1,327 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:1::\/64":[
+ {
+ "prefix":"fc00:0:2:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:2::\/64":[
+ {
+ "prefix":"fc00:0:2:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:3::\/64":[
+ {
+ "prefix":"fc00:0:2:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:4::\/64":[
+ {
+ "prefix":"fc00:0:2:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step4/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt2/step4/show_srv6_locator_table.ref
new file mode 100644
index 0000000..f339931
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step4/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:2::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:2::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt2/step4/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..5e46ddf
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step4/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step5/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step5/show_ip_route.ref
new file mode 100644
index 0000000..1d4a9e9
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step5/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step5/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step5/show_ipv6_route.ref
new file mode 100644
index 0000000..150fa92
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step5/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:1::\/64":[
+ {
+ "prefix":"fc00:0:2:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:2::\/64":[
+ {
+ "prefix":"fc00:0:2:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:3::\/64":[
+ {
+ "prefix":"fc00:0:2:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:4::\/64":[
+ {
+ "prefix":"fc00:0:2:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step5/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt2/step5/show_srv6_locator_table.ref
new file mode 100644
index 0000000..f339931
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step5/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:2::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:2::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt2/step5/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..5e46ddf
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step5/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step6/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step6/show_ip_route.ref
new file mode 100644
index 0000000..1d4a9e9
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step6/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step6/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step6/show_ipv6_route.ref
new file mode 100644
index 0000000..f9c2d09
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step6/show_ipv6_route.ref
@@ -0,0 +1,327 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:1::\/64":[
+ {
+ "prefix":"fc00:0:2:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:2::\/64":[
+ {
+ "prefix":"fc00:0:2:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:3::\/64":[
+ {
+ "prefix":"fc00:0:2:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:4::\/64":[
+ {
+ "prefix":"fc00:0:2:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step6/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt2/step6/show_srv6_locator_table.ref
new file mode 100644
index 0000000..f339931
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step6/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:2::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:2::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt2/step6/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..5e46ddf
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step6/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step7/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step7/show_ip_route.ref
new file mode 100644
index 0000000..1d4a9e9
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step7/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step7/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step7/show_ipv6_route.ref
new file mode 100644
index 0000000..150fa92
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step7/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:1::\/64":[
+ {
+ "prefix":"fc00:0:2:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:2::\/64":[
+ {
+ "prefix":"fc00:0:2:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:3::\/64":[
+ {
+ "prefix":"fc00:0:2:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:4::\/64":[
+ {
+ "prefix":"fc00:0:2:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step7/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt2/step7/show_srv6_locator_table.ref
new file mode 100644
index 0000000..f339931
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step7/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:2::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:2::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt2/step7/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..5e46ddf
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step7/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step8/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step8/show_ip_route.ref
new file mode 100644
index 0000000..1d4a9e9
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step8/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step8/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step8/show_ipv6_route.ref
new file mode 100644
index 0000000..f9c2d09
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step8/show_ipv6_route.ref
@@ -0,0 +1,327 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:1::\/64":[
+ {
+ "prefix":"fc00:0:2:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:2::\/64":[
+ {
+ "prefix":"fc00:0:2:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:3::\/64":[
+ {
+ "prefix":"fc00:0:2:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:4::\/64":[
+ {
+ "prefix":"fc00:0:2:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step8/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt2/step8/show_srv6_locator_table.ref
new file mode 100644
index 0000000..f339931
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step8/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:2::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:2::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt2/step8/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..5e46ddf
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step8/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step9/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step9/show_ip_route.ref
new file mode 100644
index 0000000..1d4a9e9
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step9/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step9/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt2/step9/show_ipv6_route.ref
new file mode 100644
index 0000000..150fa92
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step9/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:1::\/64":[
+ {
+ "prefix":"fc00:0:2:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:2::\/64":[
+ {
+ "prefix":"fc00:0:2:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:3::\/64":[
+ {
+ "prefix":"fc00:0:2:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:2:4::\/64":[
+ {
+ "prefix":"fc00:0:2:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step9/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt2/step9/show_srv6_locator_table.ref
new file mode 100644
index 0000000..f339931
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step9/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:2::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:2::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt2/step9/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..5e46ddf
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/step9/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt2/zebra.conf b/tests/topotests/isis_srv6_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..a846a6e
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt2/zebra.conf
@@ -0,0 +1,34 @@
+log file zebra.log
+!
+hostname rt2
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address fc00:0:2::1/128
+!
+interface eth-sw1
+ ip address 10.0.1.2/24
+ ipv6 address 2001:db8:1::2/64
+!
+interface eth-rt4-1
+ ip address 10.0.2.2/24
+!
+interface eth-rt4-2
+ ip address 10.0.3.2/24
+!
+segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix fc00:0:2::/48 block-len 32 node-len 16 func-bits 16
+ behavior usid
+ !
+ !
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_srv6_topo1/rt3/isisd.conf b/tests/topotests/isis_srv6_topo1/rt3/isisd.conf
new file mode 100644
index 0000000..e237db2
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/isisd.conf
@@ -0,0 +1,48 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt5-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt5-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0003.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing srv6
+ locator loc1
+ node-msd
+ max-segs-left 3
+ max-end-pop 3
+ max-h-encaps 2
+ max-end-d 5
+ interface sr0
+!
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step1/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step1/show_ip_route.ref
new file mode 100644
index 0000000..6ce5760
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step1/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step1/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..fc0e516
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step1/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:1::\/64":[
+ {
+ "prefix":"fc00:0:3:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:2::\/64":[
+ {
+ "prefix":"fc00:0:3:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:3::\/64":[
+ {
+ "prefix":"fc00:0:3:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:4::\/64":[
+ {
+ "prefix":"fc00:0:3:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step1/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt3/step1/show_srv6_locator_table.ref
new file mode 100644
index 0000000..ffa6261
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step1/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:3::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:3::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..a284240
--- /dev/null
+++ b/tests/topotests/isis_srv6_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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step2/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step2/show_ip_route.ref
new file mode 100644
index 0000000..6ce5760
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step2/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step2/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..a24d95e
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step2/show_ipv6_route.ref
@@ -0,0 +1,327 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:1::\/64":[
+ {
+ "prefix":"fc00:0:3:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:2::\/64":[
+ {
+ "prefix":"fc00:0:3:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:3::\/64":[
+ {
+ "prefix":"fc00:0:3:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:4::\/64":[
+ {
+ "prefix":"fc00:0:3:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step2/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt3/step2/show_srv6_locator_table.ref
new file mode 100644
index 0000000..ffa6261
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step2/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:3::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:3::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt3/step2/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..a284240
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step2/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step3/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step3/show_ip_route.ref
new file mode 100644
index 0000000..6ce5760
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step3/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step3/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..fc0e516
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step3/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:1::\/64":[
+ {
+ "prefix":"fc00:0:3:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:2::\/64":[
+ {
+ "prefix":"fc00:0:3:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:3::\/64":[
+ {
+ "prefix":"fc00:0:3:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:4::\/64":[
+ {
+ "prefix":"fc00:0:3:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step3/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt3/step3/show_srv6_locator_table.ref
new file mode 100644
index 0000000..ffa6261
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step3/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:3::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:3::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt3/step3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..a284240
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step3/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step4/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step4/show_ip_route.ref
new file mode 100644
index 0000000..6ce5760
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step4/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step4/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..a24d95e
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step4/show_ipv6_route.ref
@@ -0,0 +1,327 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:1::\/64":[
+ {
+ "prefix":"fc00:0:3:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:2::\/64":[
+ {
+ "prefix":"fc00:0:3:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:3::\/64":[
+ {
+ "prefix":"fc00:0:3:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:4::\/64":[
+ {
+ "prefix":"fc00:0:3:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step4/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt3/step4/show_srv6_locator_table.ref
new file mode 100644
index 0000000..ffa6261
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step4/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:3::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:3::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt3/step4/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..a284240
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step4/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step5/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step5/show_ip_route.ref
new file mode 100644
index 0000000..6ce5760
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step5/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step5/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step5/show_ipv6_route.ref
new file mode 100644
index 0000000..fc0e516
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step5/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:1::\/64":[
+ {
+ "prefix":"fc00:0:3:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:2::\/64":[
+ {
+ "prefix":"fc00:0:3:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:3::\/64":[
+ {
+ "prefix":"fc00:0:3:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:4::\/64":[
+ {
+ "prefix":"fc00:0:3:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step5/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt3/step5/show_srv6_locator_table.ref
new file mode 100644
index 0000000..ffa6261
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step5/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:3::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:3::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt3/step5/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..a284240
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step5/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step6/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step6/show_ip_route.ref
new file mode 100644
index 0000000..6ce5760
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step6/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step6/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step6/show_ipv6_route.ref
new file mode 100644
index 0000000..a24d95e
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step6/show_ipv6_route.ref
@@ -0,0 +1,327 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:1::\/64":[
+ {
+ "prefix":"fc00:0:3:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:2::\/64":[
+ {
+ "prefix":"fc00:0:3:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:3::\/64":[
+ {
+ "prefix":"fc00:0:3:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:4::\/64":[
+ {
+ "prefix":"fc00:0:3:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step6/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt3/step6/show_srv6_locator_table.ref
new file mode 100644
index 0000000..ffa6261
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step6/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:3::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:3::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt3/step6/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..a284240
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step6/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step7/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step7/show_ip_route.ref
new file mode 100644
index 0000000..6ce5760
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step7/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step7/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step7/show_ipv6_route.ref
new file mode 100644
index 0000000..fc0e516
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step7/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:1::\/64":[
+ {
+ "prefix":"fc00:0:3:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:2::\/64":[
+ {
+ "prefix":"fc00:0:3:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:3::\/64":[
+ {
+ "prefix":"fc00:0:3:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:4::\/64":[
+ {
+ "prefix":"fc00:0:3:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step7/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt3/step7/show_srv6_locator_table.ref
new file mode 100644
index 0000000..ffa6261
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step7/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:3::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:3::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt3/step7/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..a284240
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step7/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step8/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step8/show_ip_route.ref
new file mode 100644
index 0000000..6ce5760
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step8/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step8/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step8/show_ipv6_route.ref
new file mode 100644
index 0000000..a24d95e
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step8/show_ipv6_route.ref
@@ -0,0 +1,327 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:1::\/64":[
+ {
+ "prefix":"fc00:0:3:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:2::\/64":[
+ {
+ "prefix":"fc00:0:3:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:3::\/64":[
+ {
+ "prefix":"fc00:0:3:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:4::\/64":[
+ {
+ "prefix":"fc00:0:3:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step8/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt3/step8/show_srv6_locator_table.ref
new file mode 100644
index 0000000..ffa6261
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step8/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:3::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:3::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt3/step8/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..a284240
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step8/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step9/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step9/show_ip_route.ref
new file mode 100644
index 0000000..6ce5760
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step9/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "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
+ }
+ ]
+ }
+ ],
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step9/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt3/step9/show_ipv6_route.ref
new file mode 100644
index 0000000..fc0e516
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step9/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/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
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:1::\/64":[
+ {
+ "prefix":"fc00:0:3:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:2::\/64":[
+ {
+ "prefix":"fc00:0:3:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:3::\/64":[
+ {
+ "prefix":"fc00:0:3:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:3:4::\/64":[
+ {
+ "prefix":"fc00:0:3:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step9/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt3/step9/show_srv6_locator_table.ref
new file mode 100644
index 0000000..ffa6261
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step9/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:3::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:3::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt3/step9/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..a284240
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/step9/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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt3/zebra.conf b/tests/topotests/isis_srv6_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..e3bc4db
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt3/zebra.conf
@@ -0,0 +1,33 @@
+log file zebra.log
+!
+hostname rt3
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address fc00:0:3::1/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
+!
+segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix fc00:0:3::/48 block-len 32 node-len 16 func-bits 16
+ behavior usid
+ !
+ !
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_srv6_topo1/rt4/isisd.conf b/tests/topotests/isis_srv6_topo1/rt4/isisd.conf
new file mode 100644
index 0000000..b4c9214
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/isisd.conf
@@ -0,0 +1,56 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt2-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0004.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing srv6
+ locator loc1
+ node-msd
+ max-segs-left 3
+ max-end-pop 3
+ max-h-encaps 2
+ max-end-d 5
+ interface sr0
+!
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step1/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step1/show_ip_route.ref
new file mode 100644
index 0000000..0f26fa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step1/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "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.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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step1/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..2878c24
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step1/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:1::\/64":[
+ {
+ "prefix":"fc00:0:4:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:2::\/64":[
+ {
+ "prefix":"fc00:0:4:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:3::\/64":[
+ {
+ "prefix":"fc00:0:4:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:4::\/64":[
+ {
+ "prefix":"fc00:0:4:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step1/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt4/step1/show_srv6_locator_table.ref
new file mode 100644
index 0000000..189943f
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step1/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:4::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:4::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..0ca7a76
--- /dev/null
+++ b/tests/topotests/isis_srv6_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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step2/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step2/show_ip_route.ref
new file mode 100644
index 0000000..0f26fa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step2/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "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.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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step2/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..bea54ba
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step2/show_ipv6_route.ref
@@ -0,0 +1,321 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:1::\/64":[
+ {
+ "prefix":"fc00:0:4:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:2::\/64":[
+ {
+ "prefix":"fc00:0:4:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:3::\/64":[
+ {
+ "prefix":"fc00:0:4:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:4::\/64":[
+ {
+ "prefix":"fc00:0:4:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step2/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt4/step2/show_srv6_locator_table.ref
new file mode 100644
index 0000000..189943f
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step2/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:4::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:4::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt4/step2/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..0ca7a76
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step2/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step3/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step3/show_ip_route.ref
new file mode 100644
index 0000000..0f26fa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step3/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "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.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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step3/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..2878c24
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step3/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:1::\/64":[
+ {
+ "prefix":"fc00:0:4:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:2::\/64":[
+ {
+ "prefix":"fc00:0:4:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:3::\/64":[
+ {
+ "prefix":"fc00:0:4:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:4::\/64":[
+ {
+ "prefix":"fc00:0:4:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step3/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt4/step3/show_srv6_locator_table.ref
new file mode 100644
index 0000000..189943f
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step3/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:4::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:4::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt4/step3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..0ca7a76
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step3/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step4/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step4/show_ip_route.ref
new file mode 100644
index 0000000..0f26fa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step4/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "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.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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step4/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..bea54ba
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step4/show_ipv6_route.ref
@@ -0,0 +1,321 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:1::\/64":[
+ {
+ "prefix":"fc00:0:4:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:2::\/64":[
+ {
+ "prefix":"fc00:0:4:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:3::\/64":[
+ {
+ "prefix":"fc00:0:4:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:4::\/64":[
+ {
+ "prefix":"fc00:0:4:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step4/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt4/step4/show_srv6_locator_table.ref
new file mode 100644
index 0000000..189943f
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step4/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:4::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:4::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt4/step4/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..0ca7a76
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step4/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step5/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step5/show_ip_route.ref
new file mode 100644
index 0000000..0f26fa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step5/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "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.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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step5/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step5/show_ipv6_route.ref
new file mode 100644
index 0000000..2878c24
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step5/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:1::\/64":[
+ {
+ "prefix":"fc00:0:4:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:2::\/64":[
+ {
+ "prefix":"fc00:0:4:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:3::\/64":[
+ {
+ "prefix":"fc00:0:4:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:4::\/64":[
+ {
+ "prefix":"fc00:0:4:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step5/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt4/step5/show_srv6_locator_table.ref
new file mode 100644
index 0000000..189943f
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step5/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:4::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:4::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt4/step5/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..0ca7a76
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step5/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step6/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step6/show_ip_route.ref
new file mode 100644
index 0000000..0f26fa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step6/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "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.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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step6/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step6/show_ipv6_route.ref
new file mode 100644
index 0000000..bea54ba
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step6/show_ipv6_route.ref
@@ -0,0 +1,321 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:1::\/64":[
+ {
+ "prefix":"fc00:0:4:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:2::\/64":[
+ {
+ "prefix":"fc00:0:4:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:3::\/64":[
+ {
+ "prefix":"fc00:0:4:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:4::\/64":[
+ {
+ "prefix":"fc00:0:4:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step6/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt4/step6/show_srv6_locator_table.ref
new file mode 100644
index 0000000..189943f
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step6/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:4::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:4::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt4/step6/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..0ca7a76
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step6/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step7/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step7/show_ip_route.ref
new file mode 100644
index 0000000..0f26fa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step7/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "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.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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step7/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step7/show_ipv6_route.ref
new file mode 100644
index 0000000..2878c24
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step7/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:1::\/64":[
+ {
+ "prefix":"fc00:0:4:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:2::\/64":[
+ {
+ "prefix":"fc00:0:4:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:3::\/64":[
+ {
+ "prefix":"fc00:0:4:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:4::\/64":[
+ {
+ "prefix":"fc00:0:4:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step7/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt4/step7/show_srv6_locator_table.ref
new file mode 100644
index 0000000..189943f
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step7/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:4::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:4::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt4/step7/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..0ca7a76
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step7/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step8/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step8/show_ip_route.ref
new file mode 100644
index 0000000..0f26fa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step8/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "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.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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step8/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step8/show_ipv6_route.ref
new file mode 100644
index 0000000..bea54ba
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step8/show_ipv6_route.ref
@@ -0,0 +1,321 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:1::\/64":[
+ {
+ "prefix":"fc00:0:4:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:2::\/64":[
+ {
+ "prefix":"fc00:0:4:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:3::\/64":[
+ {
+ "prefix":"fc00:0:4:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:4::\/64":[
+ {
+ "prefix":"fc00:0:4:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step8/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt4/step8/show_srv6_locator_table.ref
new file mode 100644
index 0000000..189943f
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step8/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:4::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:4::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt4/step8/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..0ca7a76
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step8/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step9/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step9/show_ip_route.ref
new file mode 100644
index 0000000..0f26fa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step9/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "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.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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "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":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
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step9/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt4/step9/show_ipv6_route.ref
new file mode 100644
index 0000000..2878c24
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step9/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::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
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:1::\/64":[
+ {
+ "prefix":"fc00:0:4:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:2::\/64":[
+ {
+ "prefix":"fc00:0:4:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:3::\/64":[
+ {
+ "prefix":"fc00:0:4:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:4:4::\/64":[
+ {
+ "prefix":"fc00:0:4:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step9/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt4/step9/show_srv6_locator_table.ref
new file mode 100644
index 0000000..189943f
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step9/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:4::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:4::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt4/step9/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..0ca7a76
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/step9/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt4/zebra.conf b/tests/topotests/isis_srv6_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..551b35d
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt4/zebra.conf
@@ -0,0 +1,36 @@
+log file zebra.log
+!
+hostname rt4
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 4.4.4.4/32
+ ipv6 address fc00:0:4::1/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
+!
+segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix fc00:0:4::/48 block-len 32 node-len 16 func-bits 16
+ behavior usid
+ !
+ !
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_srv6_topo1/rt5/isisd.conf b/tests/topotests/isis_srv6_topo1/rt5/isisd.conf
new file mode 100644
index 0000000..26f895d
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/isisd.conf
@@ -0,0 +1,56 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt3-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0005.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing srv6
+ locator loc1
+ node-msd
+ max-segs-left 3
+ max-end-pop 3
+ max-h-encaps 2
+ max-end-d 5
+ interface sr0
+!
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step1/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step1/show_ip_route.ref
new file mode 100644
index 0000000..65beaa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step1/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.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":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
+ }
+ ]
+ }
+ ],
+ "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.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"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step1/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..d6ad4c2
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step1/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "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-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:1::\/64":[
+ {
+ "prefix":"fc00:0:5:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:2::\/64":[
+ {
+ "prefix":"fc00:0:5:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:3::\/64":[
+ {
+ "prefix":"fc00:0:5:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:4::\/64":[
+ {
+ "prefix":"fc00:0:5:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step1/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt5/step1/show_srv6_locator_table.ref
new file mode 100644
index 0000000..54780fc
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step1/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:5::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:5::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..f40b0d3
--- /dev/null
+++ b/tests/topotests/isis_srv6_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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step2/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step2/show_ip_route.ref
new file mode 100644
index 0000000..65beaa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step2/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.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":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
+ }
+ ]
+ }
+ ],
+ "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.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"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step2/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..c245270
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step2/show_ipv6_route.ref
@@ -0,0 +1,321 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "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-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:1::\/64":[
+ {
+ "prefix":"fc00:0:5:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:2::\/64":[
+ {
+ "prefix":"fc00:0:5:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:3::\/64":[
+ {
+ "prefix":"fc00:0:5:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:4::\/64":[
+ {
+ "prefix":"fc00:0:5:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step2/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt5/step2/show_srv6_locator_table.ref
new file mode 100644
index 0000000..54780fc
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step2/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:5::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:5::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt5/step2/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..f40b0d3
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step2/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step3/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step3/show_ip_route.ref
new file mode 100644
index 0000000..65beaa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step3/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.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":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
+ }
+ ]
+ }
+ ],
+ "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.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"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step3/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..d6ad4c2
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step3/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "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-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:1::\/64":[
+ {
+ "prefix":"fc00:0:5:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:2::\/64":[
+ {
+ "prefix":"fc00:0:5:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:3::\/64":[
+ {
+ "prefix":"fc00:0:5:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:4::\/64":[
+ {
+ "prefix":"fc00:0:5:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step3/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt5/step3/show_srv6_locator_table.ref
new file mode 100644
index 0000000..54780fc
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step3/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:5::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:5::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt5/step3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..f40b0d3
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step3/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step4/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step4/show_ip_route.ref
new file mode 100644
index 0000000..65beaa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step4/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.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":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
+ }
+ ]
+ }
+ ],
+ "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.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"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step4/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..c245270
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step4/show_ipv6_route.ref
@@ -0,0 +1,321 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "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-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:1::\/64":[
+ {
+ "prefix":"fc00:0:5:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:2::\/64":[
+ {
+ "prefix":"fc00:0:5:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:3::\/64":[
+ {
+ "prefix":"fc00:0:5:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:4::\/64":[
+ {
+ "prefix":"fc00:0:5:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step4/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt5/step4/show_srv6_locator_table.ref
new file mode 100644
index 0000000..54780fc
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step4/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:5::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:5::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt5/step4/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..f40b0d3
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step4/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step5/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step5/show_ip_route.ref
new file mode 100644
index 0000000..65beaa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step5/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.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":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
+ }
+ ]
+ }
+ ],
+ "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.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"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step5/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step5/show_ipv6_route.ref
new file mode 100644
index 0000000..d6ad4c2
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step5/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "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-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:1::\/64":[
+ {
+ "prefix":"fc00:0:5:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:2::\/64":[
+ {
+ "prefix":"fc00:0:5:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:3::\/64":[
+ {
+ "prefix":"fc00:0:5:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:4::\/64":[
+ {
+ "prefix":"fc00:0:5:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step5/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt5/step5/show_srv6_locator_table.ref
new file mode 100644
index 0000000..54780fc
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step5/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:5::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:5::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt5/step5/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..f40b0d3
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step5/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step6/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step6/show_ip_route.ref
new file mode 100644
index 0000000..65beaa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step6/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.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":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
+ }
+ ]
+ }
+ ],
+ "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.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"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step6/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step6/show_ipv6_route.ref
new file mode 100644
index 0000000..c245270
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step6/show_ipv6_route.ref
@@ -0,0 +1,321 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "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-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:1::\/64":[
+ {
+ "prefix":"fc00:0:5:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:2::\/64":[
+ {
+ "prefix":"fc00:0:5:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:3::\/64":[
+ {
+ "prefix":"fc00:0:5:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:4::\/64":[
+ {
+ "prefix":"fc00:0:5:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step6/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt5/step6/show_srv6_locator_table.ref
new file mode 100644
index 0000000..54780fc
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step6/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:5::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:5::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt5/step6/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..f40b0d3
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step6/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step7/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step7/show_ip_route.ref
new file mode 100644
index 0000000..65beaa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step7/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.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":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
+ }
+ ]
+ }
+ ],
+ "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.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"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step7/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step7/show_ipv6_route.ref
new file mode 100644
index 0000000..d6ad4c2
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step7/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "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-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:1::\/64":[
+ {
+ "prefix":"fc00:0:5:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:2::\/64":[
+ {
+ "prefix":"fc00:0:5:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:3::\/64":[
+ {
+ "prefix":"fc00:0:5:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:4::\/64":[
+ {
+ "prefix":"fc00:0:5:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step7/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt5/step7/show_srv6_locator_table.ref
new file mode 100644
index 0000000..54780fc
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step7/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:5::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:5::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt5/step7/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..f40b0d3
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step7/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step8/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step8/show_ip_route.ref
new file mode 100644
index 0000000..65beaa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step8/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.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":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
+ }
+ ]
+ }
+ ],
+ "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.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"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step8/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step8/show_ipv6_route.ref
new file mode 100644
index 0000000..c245270
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step8/show_ipv6_route.ref
@@ -0,0 +1,321 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "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-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:1::\/64":[
+ {
+ "prefix":"fc00:0:5:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:2::\/64":[
+ {
+ "prefix":"fc00:0:5:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:3::\/64":[
+ {
+ "prefix":"fc00:0:5:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:4::\/64":[
+ {
+ "prefix":"fc00:0:5:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step8/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt5/step8/show_srv6_locator_table.ref
new file mode 100644
index 0000000..54780fc
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step8/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:5::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:5::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt5/step8/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..f40b0d3
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step8/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step9/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step9/show_ip_route.ref
new file mode 100644
index 0000000..65beaa5
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step9/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "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
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.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":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
+ }
+ ]
+ }
+ ],
+ "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.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"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step9/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt5/step9/show_ipv6_route.ref
new file mode 100644
index 0000000..d6ad4c2
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step9/show_ipv6_route.ref
@@ -0,0 +1,346 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::1\/128":[
+ {
+ "prefix":"fc00:0:6::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "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-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:1::\/64":[
+ {
+ "prefix":"fc00:0:5:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:2::\/64":[
+ {
+ "prefix":"fc00:0:5:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:3::\/64":[
+ {
+ "prefix":"fc00:0:5:3::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:5:4::\/64":[
+ {
+ "prefix":"fc00:0:5:4::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step9/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt5/step9/show_srv6_locator_table.ref
new file mode 100644
index 0000000..54780fc
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step9/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:5::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:5::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt5/step9/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..f40b0d3
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/step9/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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt5/zebra.conf b/tests/topotests/isis_srv6_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..acd01bd
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt5/zebra.conf
@@ -0,0 +1,36 @@
+log file zebra.log
+!
+hostname rt5
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 5.5.5.5/32
+ ipv6 address fc00:0:5::1/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
+!
+segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix fc00:0:5::/48 block-len 32 node-len 16 func-bits 16
+ behavior usid
+ !
+ !
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_srv6_topo1/rt6/isisd.conf b/tests/topotests/isis_srv6_topo1/rt6/isisd.conf
new file mode 100644
index 0000000..f8816db
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/isisd.conf
@@ -0,0 +1,42 @@
+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-interval 1
+ isis hello-multiplier 10
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0006.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing srv6
+ locator loc1
+ node-msd
+ max-segs-left 3
+ max-end-pop 3
+ max-h-encaps 2
+ max-end-d 5
+ interface sr0
+!
diff --git a/tests/topotests/isis_srv6_topo1/rt6/sharpd.conf b/tests/topotests/isis_srv6_topo1/rt6/sharpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/sharpd.conf
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step1/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step1/show_ip_route.ref
new file mode 100644
index 0000000..5fc293b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step1/show_ip_route.ref
@@ -0,0 +1,273 @@
+{
+ "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.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_srv6_topo1/rt6/step1/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..407e2d0
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step1/show_ipv6_route.ref
@@ -0,0 +1,268 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:1::\/64":[
+ {
+ "prefix":"fc00:0:6:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:2::\/64":[
+ {
+ "prefix":"fc00:0:6:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step1/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt6/step1/show_srv6_locator_table.ref
new file mode 100644
index 0000000..35aa61d
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step1/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:6::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:6::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..8300ca0
--- /dev/null
+++ b/tests/topotests/isis_srv6_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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step2/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step2/show_ip_route.ref
new file mode 100644
index 0000000..5fc293b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step2/show_ip_route.ref
@@ -0,0 +1,273 @@
+{
+ "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.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_srv6_topo1/rt6/step2/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..6c64bd7
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step2/show_ipv6_route.ref
@@ -0,0 +1,243 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:1::\/64":[
+ {
+ "prefix":"fc00:0:6:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:2::\/64":[
+ {
+ "prefix":"fc00:0:6:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step2/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt6/step2/show_srv6_locator_table.ref
new file mode 100644
index 0000000..35aa61d
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step2/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:6::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:6::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt6/step2/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..8300ca0
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step2/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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step3/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step3/show_ip_route.ref
new file mode 100644
index 0000000..5fc293b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step3/show_ip_route.ref
@@ -0,0 +1,273 @@
+{
+ "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.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_srv6_topo1/rt6/step3/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..407e2d0
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step3/show_ipv6_route.ref
@@ -0,0 +1,268 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:1::\/64":[
+ {
+ "prefix":"fc00:0:6:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:2::\/64":[
+ {
+ "prefix":"fc00:0:6:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step3/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt6/step3/show_srv6_locator_table.ref
new file mode 100644
index 0000000..35aa61d
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step3/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:6::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:6::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt6/step3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..8300ca0
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step3/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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step4/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step4/show_ip_route.ref
new file mode 100644
index 0000000..5fc293b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step4/show_ip_route.ref
@@ -0,0 +1,273 @@
+{
+ "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.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_srv6_topo1/rt6/step4/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..6c64bd7
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step4/show_ipv6_route.ref
@@ -0,0 +1,243 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:1::\/64":[
+ {
+ "prefix":"fc00:0:6:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:2::\/64":[
+ {
+ "prefix":"fc00:0:6:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step4/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt6/step4/show_srv6_locator_table.ref
new file mode 100644
index 0000000..35aa61d
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step4/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:6::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:6::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt6/step4/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..8300ca0
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step4/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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step5/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step5/show_ip_route.ref
new file mode 100644
index 0000000..5fc293b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step5/show_ip_route.ref
@@ -0,0 +1,273 @@
+{
+ "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.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_srv6_topo1/rt6/step5/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step5/show_ipv6_route.ref
new file mode 100644
index 0000000..407e2d0
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step5/show_ipv6_route.ref
@@ -0,0 +1,268 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:1::\/64":[
+ {
+ "prefix":"fc00:0:6:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:2::\/64":[
+ {
+ "prefix":"fc00:0:6:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step5/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt6/step5/show_srv6_locator_table.ref
new file mode 100644
index 0000000..35aa61d
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step5/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:6::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:6::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt6/step5/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..8300ca0
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step5/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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step6/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step6/show_ip_route.ref
new file mode 100644
index 0000000..5fc293b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step6/show_ip_route.ref
@@ -0,0 +1,273 @@
+{
+ "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.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_srv6_topo1/rt6/step6/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step6/show_ipv6_route.ref
new file mode 100644
index 0000000..6c64bd7
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step6/show_ipv6_route.ref
@@ -0,0 +1,243 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:1::\/64":[
+ {
+ "prefix":"fc00:0:6:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:2::\/64":[
+ {
+ "prefix":"fc00:0:6:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step6/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt6/step6/show_srv6_locator_table.ref
new file mode 100644
index 0000000..35aa61d
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step6/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:6::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:6::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt6/step6/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..8300ca0
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step6/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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step7/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step7/show_ip_route.ref
new file mode 100644
index 0000000..5fc293b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step7/show_ip_route.ref
@@ -0,0 +1,273 @@
+{
+ "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.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_srv6_topo1/rt6/step7/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step7/show_ipv6_route.ref
new file mode 100644
index 0000000..407e2d0
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step7/show_ipv6_route.ref
@@ -0,0 +1,268 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:1::\/64":[
+ {
+ "prefix":"fc00:0:6:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:2::\/64":[
+ {
+ "prefix":"fc00:0:6:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step7/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt6/step7/show_srv6_locator_table.ref
new file mode 100644
index 0000000..35aa61d
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step7/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:6::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:6::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt6/step7/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..8300ca0
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step7/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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step8/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step8/show_ip_route.ref
new file mode 100644
index 0000000..5fc293b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step8/show_ip_route.ref
@@ -0,0 +1,273 @@
+{
+ "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.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_srv6_topo1/rt6/step8/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step8/show_ipv6_route.ref
new file mode 100644
index 0000000..6c64bd7
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step8/show_ipv6_route.ref
@@ -0,0 +1,243 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:1::\/64":[
+ {
+ "prefix":"fc00:0:6:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:2::\/64":[
+ {
+ "prefix":"fc00:0:6:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step8/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt6/step8/show_srv6_locator_table.ref
new file mode 100644
index 0000000..35aa61d
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step8/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:6::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:6::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt6/step8/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..8300ca0
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step8/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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step9/show_ip_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step9/show_ip_route.ref
new file mode 100644
index 0000000..5fc293b
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step9/show_ip_route.ref
@@ -0,0 +1,273 @@
+{
+ "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.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_srv6_topo1/rt6/step9/show_ipv6_route.ref b/tests/topotests/isis_srv6_topo1/rt6/step9/show_ipv6_route.ref
new file mode 100644
index 0000000..407e2d0
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step9/show_ipv6_route.ref
@@ -0,0 +1,268 @@
+{
+ "fc00:0:1::1\/128":[
+ {
+ "prefix":"fc00:0:1::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
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::1\/128":[
+ {
+ "prefix":"fc00:0:2::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::1\/128":[
+ {
+ "prefix":"fc00:0:3::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::1\/128":[
+ {
+ "prefix":"fc00:0:4::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::1\/128":[
+ {
+ "prefix":"fc00:0:5::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:1::\/48":[
+ {
+ "prefix":"fc00:0:1::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:2::\/48":[
+ {
+ "prefix":"fc00:0:2::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:3::\/48":[
+ {
+ "prefix":"fc00:0:3::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:4::\/48":[
+ {
+ "prefix":"fc00:0:4::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:5::\/48":[
+ {
+ "prefix":"fc00:0:5::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fc00:0:6::\/48":[
+ {
+ "prefix":"fc00:0:6::\/48",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"sr0",
+ "active":true,
+ "seg6local":{
+ "action":"End"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:1::\/64":[
+ {
+ "prefix":"fc00:0:6:1::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ],
+ "fc00:0:6:2::\/64":[
+ {
+ "prefix":"fc00:0:6:2::\/64",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "active":true,
+ "seg6local":{
+ "action":"End.X"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step9/show_srv6_locator_table.ref b/tests/topotests/isis_srv6_topo1/rt6/step9/show_srv6_locator_table.ref
new file mode 100644
index 0000000..35aa61d
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step9/show_srv6_locator_table.ref
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name":"loc1",
+ "prefix":"fc00:0:6::/48",
+ "blockBitsLength":32,
+ "nodeBitsLength":16,
+ "functionBitsLength":16,
+ "argumentBitsLength":0,
+ "statusUp":true,
+ "chunks":[
+ {
+ "prefix":"fc00:0:6::/48",
+ "proto":"isis"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_srv6_topo1/rt6/step9/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..8300ca0
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/step9/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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_srv6_topo1/rt6/zebra.conf b/tests/topotests/isis_srv6_topo1/rt6/zebra.conf
new file mode 100644
index 0000000..83e7f52
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/rt6/zebra.conf
@@ -0,0 +1,36 @@
+log file zebra.log
+!
+hostname rt6
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 6.6.6.6/32
+ ipv6 address fc00:0:6::1/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.10.1/24
+ ip address 2001:db8:10::1/64
+!
+segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix fc00:0:6::/48 block-len 32 node-len 16 func-bits 16
+ behavior usid
+ !
+ !
+!
+ip forwarding
+!
+ip route fc00:0:9::1/128 2001:db8:10::2
+!
+line vty
+!
diff --git a/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py b/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py
new file mode 100644
index 0000000..892f6e1
--- /dev/null
+++ b/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py
@@ -0,0 +1,1124 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2023 by
+# Carmine Scarpitta <carmine.scarpitta@uniroma2.it>
+#
+
+"""
+test_isis_srv6_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.10.0/24
+ |
+ |eth-rt6 (.2)
+ +---------+
+ | |
+ | DST |
+ | 9.9.9.2 |
+ | |
+ +---------+
+
+"""
+
+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,
+ create_interface_in_kernel,
+)
+
+pytestmark = [pytest.mark.isisd, pytest.mark.sharpd]
+
+
+def build_topo(tgen):
+ """Build function"""
+
+ # Define FRR Routers
+ tgen.add_router("rt1")
+ tgen.add_router("rt2")
+ tgen.add_router("rt3")
+ tgen.add_router("rt4")
+ tgen.add_router("rt5")
+ tgen.add_router("rt6")
+ tgen.add_router("dst")
+
+ # 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")
+
+ # Add dummy interface for SRv6
+ create_interface_in_kernel(
+ tgen,
+ "rt1",
+ "sr0",
+ "2001:db8::1",
+ netmask="128",
+ create=True,
+ )
+ create_interface_in_kernel(
+ tgen,
+ "rt2",
+ "sr0",
+ "2001:db8::2",
+ netmask="128",
+ create=True,
+ )
+ create_interface_in_kernel(
+ tgen,
+ "rt3",
+ "sr0",
+ "2001:db8::3",
+ netmask="128",
+ create=True,
+ )
+ create_interface_in_kernel(
+ tgen,
+ "rt4",
+ "sr0",
+ "2001:db8::4",
+ netmask="128",
+ create=True,
+ )
+ create_interface_in_kernel(
+ tgen,
+ "rt5",
+ "sr0",
+ "2001:db8::5",
+ netmask="128",
+ create=True,
+ )
+ create_interface_in_kernel(
+ tgen,
+ "rt6",
+ "sr0",
+ "2001:db8::6",
+ netmask="128",
+ create=True,
+ )
+
+
+def setup_module(mod):
+ """Sets up the pytest environment"""
+
+ # Verify if kernel requirements are satisfied
+ result = required_linux_kernel_version("4.10")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ # Build the topology
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ # For all registered routers, load the zebra and isis configuration files
+ 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)))
+ if (os.path.exists('{}/sharpd.conf'.format(rname))):
+ router.load_config(TopoRouter.RD_SHARP,
+ os.path.join(CWD, '{}/sharpd.conf'.format(rname)))
+
+ # Start routers
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+
+ # Teardown the topology
+ tgen = get_topogen()
+ 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 = functools.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 check_ping6(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"
+
+
+#
+# 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_srv6_locator_step1():
+ logger.info("Test (step 1): verify SRv6 Locator")
+ 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 segment-routing srv6 locator json", "step1/show_srv6_locator_table.ref"
+ )
+
+
+def test_ping_step1():
+ logger.info("Test (step 1): verify ping")
+ tgen = get_topogen()
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("6.1")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met, kernel version should be >=6.1")
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Setup encap route on rt1, decap route on rt2
+ tgen.gears["rt1"].vtysh_cmd("sharp install seg6-routes fc00:0:9::1 nexthop-seg6 2001:db8:1::2 encap fc00:0:1:2:6:f00d:: 1")
+ tgen.gears["rt6"].vtysh_cmd("sharp install seg6local-routes fc00:0:f00d:: nexthop-seg6local eth-dst End_DT6 254 1")
+ tgen.gears["dst"].vtysh_cmd("sharp install route 2001:db8:1::1 nexthop 2001:db8:10::1 1")
+
+ # Try to ping dst from rt1
+ check_ping6("rt1", "fc00:0:9::1", True)
+
+
+#
+# Step 2
+#
+# Action(s):
+# -Disable SRv6 Locator on zebra on rt1
+#
+# Expected changes:
+# -rt1 should uninstall the SRv6 End SID
+# -rt1 should remove the SRv6 Locator from zebra
+# -rt1 should remove the SRv6 Locator TLV from the LSPs
+# -rt1 should remove the SRv6 Capabilities Sub-TLV from the Router Capability TLV
+# -rt2, rt3, rt4, rt5, rt6 should uninstall the route pointing to the rt1's SRv6 Locator from the RIB
+#
+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 SRv6 Locator on zebra on rt1")
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ no locator loc1
+ """
+ )
+
+ 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_srv6_locator_step2():
+ logger.info("Test (step 2): verify SRv6 Locator")
+ 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 segment-routing srv6 locator json", "step2/show_srv6_locator_table.ref"
+ )
+
+
+def test_ping_step2():
+ logger.info("Test (step 2): verify ping")
+ tgen = get_topogen()
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("6.1")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met, kernel version should be >=6.1")
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_ping6("rt1", "fc00:0:9::1", False)
+
+
+#
+# Step 3
+#
+# Action(s):
+# -Enable SRv6 Locator on zebra on rt1
+#
+# Expected changes:
+# -rt1 should install the SRv6 End SID
+# -rt1 should install the SRv6 Locator in zebra
+# -rt1 should add the SRv6 Locator TLV to the LSPs
+# -rt1 should add the SRv6 Capabilities Sub-TLV to the Router Capability TLV
+# -rt2, rt3, rt4, rt5, rt6 should install a route pointing to the rt1's SRv6 Locator in the RIB
+#
+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("Enabling SRv6 Locator on zebra on rt1")
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix fc00:0:1::/48 block-len 32 node-len 16 func-bits 16
+ behavior usid
+ """
+ )
+
+ 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_srv6_locator_step3():
+ logger.info("Test (step 3): verify SRv6 Locator")
+ 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 segment-routing srv6 locator json", "step3/show_srv6_locator_table.ref"
+ )
+
+
+def test_ping_step3():
+ logger.info("Test (step 3): verify ping")
+ tgen = get_topogen()
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("6.1")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met, kernel version should be >=6.1")
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_ping6("rt1", "fc00:0:9::1", True)
+
+
+#
+# Step 4
+#
+# Action(s):
+# -Disable SRv6 Locator on ISIS on rt1
+#
+# Expected changes:
+# -rt1 should uninstall the SRv6 End SID
+# -rt1 should remove the SRv6 Locator TLV from the LSPs
+# -rt1 should remove the SRv6 Capabilities Sub-TLV from the Router Capability TLV
+# -rt2, rt3, rt4, rt5, rt6 should uninstall the route pointing to the rt1's SRv6 Locator from the RIB
+#
+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("Disabling SRv6 Locator on ISIS on rt1")
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ segment-routing srv6
+ no locator loc1
+ """
+ )
+
+ 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_srv6_locator_step4():
+ logger.info("Test (step 4): verify SRv6 Locator")
+ 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 segment-routing srv6 locator json", "step4/show_srv6_locator_table.ref"
+ )
+
+
+def test_ping_step4():
+ logger.info("Test (step 4): verify ping")
+ tgen = get_topogen()
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("6.1")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met, kernel version should be >=6.1")
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_ping6("rt1", "fc00:0:9::1", False)
+
+
+#
+# Step 5
+#
+# Action(s):
+# -Enable SRv6 Locator on ISIS on rt1
+#
+# Expected changes:
+# -rt1 should install the SRv6 End SID
+# -rt1 should add the SRv6 Locator TLV to the LSPs
+# -rt1 should add the SRv6 Capabilities Sub-TLV to the Router Capability TLV
+# -rt2, rt3, rt4, rt5, rt6 should install a route pointing to the rt1's SRv6 Locator in the RIB
+#
+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("Enabling SRv6 Locator on ISIS on rt1")
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ segment-routing srv6
+ locator loc1
+ """
+ )
+
+ 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_srv6_locator_step5():
+ logger.info("Test (step 5): verify SRv6 Locator")
+ 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 segment-routing srv6 locator json", "step5/show_srv6_locator_table.ref"
+ )
+
+
+def test_ping_step5():
+ logger.info("Test (step 5): verify ping")
+ tgen = get_topogen()
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("6.1")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met, kernel version should be >=6.1")
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_ping6("rt1", "fc00:0:9::1", True)
+
+
+#
+# Step 6
+#
+# Action(s):
+# -Disable SRv6 on ISIS on rt1
+#
+# Expected changes:
+# -rt1 should uninstall the SRv6 End SID
+# -rt1 should remove the SRv6 Locator TLV from the LSPs
+# -rt1 should remove the SRv6 Capabilities Sub-TLV from the Router Capability TLV
+# -rt2, rt3, rt4, rt5, rt6 should uninstall the route pointing to the rt1's SRv6 Locator from the RIB
+#
+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("Disabling SRv6 on ISIS on rt1")
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ no segment-routing srv6
+ """
+ )
+
+ 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_srv6_locator_step6():
+ logger.info("Test (step 6): verify SRv6 Locator")
+ 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 segment-routing srv6 locator json", "step6/show_srv6_locator_table.ref"
+ )
+
+
+def test_ping_step6():
+ logger.info("Test (step 6): verify ping")
+ tgen = get_topogen()
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("6.1")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met, kernel version should be >=6.1")
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_ping6("rt1", "fc00:0:9::1", False)
+
+
+#
+# Step 7
+#
+# Action(s):
+# -Enable SRv6 on ISIS on rt1
+#
+# Expected changes:
+# -rt1 should install the SRv6 End SID
+# -rt1 should add the SRv6 Locator TLV to the LSPs
+# -rt1 should add the SRv6 Capabilities Sub-TLV to the Router Capability TLV
+# -rt2, rt3, rt4, rt5, rt6 should install a route pointing to the rt1's SRv6 Locator in the RIB
+#
+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("Enabling SRv6 on ISIS on rt1")
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ segment-routing srv6
+ locator loc1
+ """
+ )
+
+ 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_srv6_locator_step7():
+ logger.info("Test (step 7): verify SRv6 Locator")
+ 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 segment-routing srv6 locator json", "step7/show_srv6_locator_table.ref"
+ )
+
+
+def test_ping_step7():
+ logger.info("Test (step 7): verify ping")
+ tgen = get_topogen()
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("6.1")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met, kernel version should be >=6.1")
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_ping6("rt1", "fc00:0:9::1", True)
+
+
+#
+# Step 8
+#
+# Action(s):
+# -Disable SRv6 on zebra on rt1
+#
+# Expected changes:
+# -rt1 should uninstall the SRv6 End SID
+# -rt1 should remove the SRv6 Locator TLV from the LSPs
+# -rt1 should remove the SRv6 Capabilities Sub-TLV from the Router Capability TLV
+# -rt2, rt3, rt4, rt5, rt6 should uninstall the route pointing to the rt1's SRv6 Locator from the RIB
+#
+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("Disabling SRv6 on zebra on rt1")
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ no srv6
+ """
+ )
+
+ 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_srv6_locator_step8():
+ logger.info("Test (step 8): verify SRv6 Locator")
+ 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 segment-routing srv6 locator json", "step8/show_srv6_locator_table.ref"
+ )
+
+
+def test_ping_step8():
+ logger.info("Test (step 8): verify ping")
+ tgen = get_topogen()
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("6.1")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met, kernel version should be >=6.1")
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_ping6("rt1", "fc00:0:9::1", False)
+
+
+#
+# Step 9
+#
+# Action(s):
+# -Enable SRv6 on zebra on rt1
+#
+# Expected changes:
+# -rt1 should install the SRv6 End SID
+# -rt1 should add the SRv6 Locator TLV to the LSPs
+# -rt1 should add the SRv6 Capabilities Sub-TLV to the Router Capability TLV
+# -rt2, rt3, rt4, rt5, rt6 should install a route pointing to the rt1's SRv6 Locator in the RIB
+#
+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("Enabling SRv6 on zebra on rt1")
+ tgen.gears["rt1"].vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix fc00:0:1::/48 block-len 32 node-len 16 func-bits 16
+ behavior usid
+ """
+ )
+
+ 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_srv6_locator_step9():
+ logger.info("Test (step 9): verify SRv6 Locator")
+ 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 segment-routing srv6 locator json", "step9/show_srv6_locator_table.ref"
+ )
+
+
+def test_ping_step9():
+ logger.info("Test (step 9): verify ping")
+ tgen = get_topogen()
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("6.1")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met, kernel version should be >=6.1")
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_ping6("rt1", "fc00:0:9::1", True)
+
+
+# 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..faa6c30
--- /dev/null
+++ b/tests/topotests/isis_te_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-interval 1
+ isis hello-multiplier 10
+!
+interface r1-eth1
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..2e96d5c
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/r1/zebra.conf
@@ -0,0 +1,26 @@
+!
+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
+ max-bw 10e+10
+ 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..ed10591
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/r2/isisd.conf
@@ -0,0 +1,49 @@
+!
+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-interval 1
+ isis hello-multiplier 10
+!
+interface r2-eth1
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface r2-eth2
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+interface r2-eth3
+ ip router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+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..a25b785
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/r2/zebra.conf
@@ -0,0 +1,40 @@
+!
+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
+ max-bw 10e+10
+ 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..c689e84
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/r3/isisd.conf
@@ -0,0 +1,36 @@
+!
+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-interval 1
+ isis hello-multiplier 10
+!
+interface r3-eth1
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+!
+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..ba522cc
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/r4/isisd.conf
@@ -0,0 +1,41 @@
+!
+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-interval 1
+ isis hello-multiplier 10
+!
+interface r4-eth1
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+!
+!
+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..85ced41
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step1.json
@@ -0,0 +1,838 @@
+{
+ "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":"2001:db8:1::1:1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:1::1: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:1::1:2",
+ "remote-address-v6":"2001:db8:1::1:1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:5::3:4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"2001:db8:5::4:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.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":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.4.4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "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: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_step10.json b/tests/topotests/isis_te_topo1/reference/ted_step10.json
new file mode 100644
index 0000000..9348f9c
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step10.json
@@ -0,0 +1,967 @@
+{
+ "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":"2001:db8::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,
+ "extAdminGroup":{
+ "words":[
+ "0x80000001",
+ "0x00000000",
+ "0x00000000",
+ "0x00000000",
+ "0x00000001"
+ ],
+ "bitPositions":[
+ 0,
+ 31,
+ 128
+ ]
+ },
+ "local-address-v6":"2001:db8::1",
+ "remote-address-v6":"2001:db8::2",
+ "max-link-bandwidth":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"2001:db8::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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:1::1:1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:1::1: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:1::1:2",
+ "remote-address-v6":"2001:db8:1::1:1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:5::3:4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"2001:db8:5::4:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":"10.0.0.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,
+ "extAdminGroup":{
+ "words":[
+ "0x80000001",
+ "0x00000000",
+ "0x00000000",
+ "0x00000000",
+ "0x00000001"
+ ],
+ "bitPositions":[
+ 0,
+ 31,
+ 128
+ ]
+ },
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.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":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000
+ }
+ },
+ {
+ "edge-id":"10.0.4.4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":20000,
+ "jitter":10000
+ },
+ "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\/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_step2.json b/tests/topotests/isis_te_topo1/reference/ted_step2.json
new file mode 100644
index 0000000..a84f4c0
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step2.json
@@ -0,0 +1,642 @@
+{
+ "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":"2001:db8:3::3:2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:5::3:4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"2001:db8:5::4:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.4.4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "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.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..fb3cdb9
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step3.json
@@ -0,0 +1,742 @@
+{
+ "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":"2001:db8::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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"2001:db8::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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:5::3:4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"2001:db8:5::4:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.4.4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "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.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..fb3cdb9
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step4.json
@@ -0,0 +1,742 @@
+{
+ "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":"2001:db8::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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"2001:db8::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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:5::3:4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"2001:db8:5::4:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.4.4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "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.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..edc90c3
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step5.json
@@ -0,0 +1,938 @@
+{
+ "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":"2001:db8::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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"2001:db8::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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:1::1:1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:1::1: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:1::1:2",
+ "remote-address-v6":"2001:db8:1::1:1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:5::3:4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"2001:db8:5::4:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.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":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.4.4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "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\/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..ec65972
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step6.json
@@ -0,0 +1,939 @@
+{
+ "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":"2001:db8::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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"2001:db8::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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:1::1:1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:1::1: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:1::1:2",
+ "remote-address-v6":"2001:db8:1::1:1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:5::3:4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"2001:db8:5::4:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.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":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000
+ }
+ },
+ {
+ "edge-id":"10.0.4.4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":20000,
+ "jitter":10000
+ },
+ "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\/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_step7.json b/tests/topotests/isis_te_topo1/reference/ted_step7.json
new file mode 100644
index 0000000..6177008
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step7.json
@@ -0,0 +1,969 @@
+{
+ "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":"2001:db8::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,
+ "extAdminGroup":{
+ "words":[
+ "0x80000001",
+ "0x00000001",
+ "0x00000000",
+ "0x00000000",
+ "0x00000001"
+ ],
+ "bitPositions":[
+ 0,
+ 31,
+ 32,
+ 128
+ ]
+ },
+ "local-address-v6":"2001:db8::1",
+ "remote-address-v6":"2001:db8::2",
+ "max-link-bandwidth":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"2001:db8::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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:1::1:1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:1::1: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:1::1:2",
+ "remote-address-v6":"2001:db8:1::1:1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:5::3:4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"2001:db8:5::4:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":"10.0.0.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,
+ "extAdminGroup":{
+ "words":[
+ "0x80000001",
+ "0x00000001",
+ "0x00000000",
+ "0x00000000",
+ "0x00000001"
+ ],
+ "bitPositions":[
+ 0,
+ 31,
+ 32,
+ 128
+ ]
+ },
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.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":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000
+ }
+ },
+ {
+ "edge-id":"10.0.4.4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":20000,
+ "jitter":10000
+ },
+ "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\/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_step8.json b/tests/topotests/isis_te_topo1/reference/ted_step8.json
new file mode 100644
index 0000000..2cf17f7
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step8.json
@@ -0,0 +1,969 @@
+{
+ "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":"2001:db8::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,
+ "extAdminGroup":{
+ "words":[
+ "0x80000001",
+ "0x00000002",
+ "0x00000000",
+ "0x00000000",
+ "0x00000001"
+ ],
+ "bitPositions":[
+ 0,
+ 31,
+ 33,
+ 128
+ ]
+ },
+ "local-address-v6":"2001:db8::1",
+ "remote-address-v6":"2001:db8::2",
+ "max-link-bandwidth":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"2001:db8::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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:1::1:1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:1::1: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:1::1:2",
+ "remote-address-v6":"2001:db8:1::1:1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:3::3:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"2001:db8:5::3:4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"2001:db8:5::4:3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":"10.0.0.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,
+ "extAdminGroup":{
+ "words":[
+ "0x80000001",
+ "0x00000002",
+ "0x00000000",
+ "0x00000000",
+ "0x00000001"
+ ],
+ "bitPositions":[
+ 0,
+ 31,
+ 33,
+ 128
+ ]
+ },
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.0.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":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.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":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.3",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000
+ }
+ },
+ {
+ "edge-id":"10.0.4.4",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":20000,
+ "jitter":10000
+ },
+ "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\/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_step9.json b/tests/topotests/isis_te_topo1/reference/ted_step9.json
new file mode 120000
index 0000000..1c01a91
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step9.json
@@ -0,0 +1 @@
+ted_step8.json \ No newline at end of file
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..9c70e05
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/test_isis_te_topo1.py
@@ -0,0 +1,306 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_isis_te_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by Orange
+# Author: Olivier Dugeon <olivier.dugeon@orange.com>
+#
+
+"""
+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_step7():
+ "Step7: Set extended admin-group on r1-eth0"
+
+ tgen = setup_testcase("Step7: Modify link parameters on r1")
+
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "affinity-map WHITE bit-position 0"')
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "affinity-map RED bit-position 31"')
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "affinity-map GREEN bit-position 32"')
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "affinity-map BLACK bit-position 128"')
+
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "interface r1-eth0" -c "link-params" -c "affinity RED WHITE BLACK GREEN"'
+ )
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step7.json")
+
+
+def test_step8():
+ "Step8: Change value of affinity-map GREEN"
+
+ tgen = setup_testcase("Step8: Change value of affinity-map GREEN")
+
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "affinity-map GREEN bit-position 33"')
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step8.json")
+
+
+def test_step9():
+ "Step9: Trying to remove affinity-map GREEN. \
+ Must not succeed because in use"
+
+ tgen = setup_testcase("Step9: Trying to remove affinity-map GREEN")
+
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "no affinity-map GREEN"')
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step9.json")
+
+
+def test_step10():
+ "Step10: Removing r1-eth0 affinity GREEN"
+
+ tgen = setup_testcase("Step10: Removing r1-eth0 affinity GREEN")
+
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "interface r1-eth0" -c "link-params" -c "no affinity GREEN"'
+ )
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step10.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..1218ed3
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/isisd.conf
@@ -0,0 +1,35 @@
+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
+! debug isis ti-lfa
+!
+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-interval 1
+ isis hello-multiplier 10
+ 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..9c5901b
--- /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": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step10/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step10/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step10/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step10/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step10/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step10/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step10/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step10/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step10/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step11/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step11/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step11/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step11/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step11/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step11/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step11/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step11/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step11/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step12/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step12/show_ip_route.ref.diff
new file mode 100644
index 0000000..a8d6e6c
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step12/show_ip_route.ref.diff
@@ -0,0 +1,19 @@
+--- a/rt1/step11/show_ip_route.ref
++++ b/rt1/step12/show_ip_route.ref
+@@ -110,16 +110,6 @@
+ "labels":[
+ 16060
+ ]
+- },
+- {
+- "fib":true,
+- "ip":"10.0.1.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16060
+- ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step12/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step12/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..637c59f
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step12/show_ipv6_route.ref.diff
@@ -0,0 +1,18 @@
+--- a/rt1/step11/show_ipv6_route.ref
++++ b/rt1/step12/show_ipv6_route.ref
+@@ -105,15 +105,6 @@
+ "labels":[
+ 16061
+ ]
+- },
+- {
+- "fib":true,
+- "afi":"ipv6",
+- "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16061
+- ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step12/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step12/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e110bf4
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step12/show_mpls_table.ref.diff
@@ -0,0 +1,28 @@
+--- a/rt1/step11/show_mpls_table.ref
++++ b/rt1/step12/show_mpls_table.ref
+@@ -79,12 +79,6 @@
+ "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"
+ }
+ ]
+@@ -96,12 +90,6 @@
+ {
+ "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/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..4de9d8c
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/zebra.conf
@@ -0,0 +1,20 @@
+log file zebra.log
+!
+hostname rt1
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+! debug zebra rib detail
+!
+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..aed6971
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/isisd.conf
@@ -0,0 +1,49 @@
+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
+! debug isis ti-lfa
+!
+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-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute ti-lfa
+!
+interface eth-rt4-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute ti-lfa
+!
+interface eth-rt4-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..7d9463d
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 100,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step10/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step10/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step10/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step10/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step10/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step10/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step10/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step10/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step10/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step11/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step11/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step11/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step11/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step11/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step11/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step11/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step11/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step11/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step12/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step12/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step12/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step12/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step12/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step12/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step12/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step12/show_mpls_table.ref.diff
new file mode 100644
index 0000000..84a3644
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step12/show_mpls_table.ref.diff
@@ -0,0 +1,20 @@
+--- a/rt2/step11/show_mpls_table.ref
++++ b/rt2/step12/show_mpls_table.ref
+@@ -199,7 +199,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16060,
++ "outLabel":16500,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+@@ -230,7 +230,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16061,
++ "outLabel":16501,
+ "interface":"eth-sw1"
+ }
+ ]
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..f2975f6
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/zebra.conf
@@ -0,0 +1,26 @@
+log file zebra.log
+!
+hostname rt2
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+! debug zebra rib detail
+!
+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..d2f01e4
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/isisd.conf
@@ -0,0 +1,49 @@
+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
+! debug isis ti-lfa
+!
+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-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute ti-lfa
+!
+interface eth-rt5-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute ti-lfa
+!
+interface eth-rt5-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..777c749
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 100,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 10,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step10/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step10/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step10/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step10/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step10/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step10/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step10/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step10/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step10/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step11/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step11/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step11/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step11/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step11/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step11/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step11/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step11/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step11/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step12/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step12/show_ip_route.ref.diff
new file mode 100644
index 0000000..8695cf8
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step12/show_ip_route.ref.diff
@@ -0,0 +1,58 @@
+--- a/rt3/step11/show_ip_route.ref
++++ b/rt3/step12/show_ip_route.ref
+@@ -198,44 +198,37 @@
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+- "metric":30,
++ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+- "ip":"10.0.4.5",
++ "ip":"10.0.1.2",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt5-1",
++ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+- 30060
++ 16060
+ ]
+ },
+ {
+ "fib":true,
+- "ip":"10.0.5.5",
++ "ip":"10.0.4.5",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt5-2",
++ "interfaceName":"eth-rt5-1",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 30060
+ ]
+- }
+- ],
+- "backupNexthops":[
++ },
+ {
+- "ip":"10.0.1.2",
++ "fib":true,
++ "ip":"10.0.5.5",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1",
++ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+- 16060
++ 30060
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step12/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step12/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..661d0fe
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step12/show_ipv6_route.ref.diff
@@ -0,0 +1,45 @@
+--- a/rt3/step11/show_ipv6_route.ref
++++ b/rt3/step12/show_ipv6_route.ref
+@@ -186,7 +186,7 @@
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+- "metric":30,
++ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+@@ -194,9 +194,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 30061
+ ]
+@@ -206,23 +203,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 30061
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16061
+- ]
+- }
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step12/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step12/show_mpls_table.ref.diff
new file mode 100644
index 0000000..30941b3
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step12/show_mpls_table.ref.diff
@@ -0,0 +1,60 @@
+--- a/rt3/step11/show_mpls_table.ref
++++ b/rt3/step12/show_mpls_table.ref
+@@ -165,27 +165,8 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":30060,
+- "installed":true,
+- "nexthop":"10.0.5.5",
+- "backupIndex":[
+- 0
+- ]
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":30060,
+- "installed":true,
+- "nexthop":"10.0.4.5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+ "outLabel":16060,
++ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+@@ -196,27 +177,8 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":30061,
+- "installed":true,
+- "interface":"eth-rt5-2",
+- "backupIndex":[
+- 0
+- ]
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":30061,
+- "installed":true,
+- "interface":"eth-rt5-1",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+ "outLabel":16061,
++ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
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..7ffdcd6
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/zebra.conf
@@ -0,0 +1,26 @@
+log file zebra.log
+!
+hostname rt3
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+! debug zebra rib detail
+!
+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..c1117b5
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/isisd.conf
@@ -0,0 +1,58 @@
+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
+! debug isis ti-lfa
+!
+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-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute ti-lfa
+!
+interface eth-rt2-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute ti-lfa
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute ti-lfa
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..0ca7a76
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step10/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step10/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step10/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step10/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step10/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step10/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step10/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step10/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step10/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step11/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step11/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step11/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step11/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step11/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step11/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step11/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step11/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step11/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step12/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step12/show_ip_route.ref.diff
new file mode 100644
index 0000000..2645c59
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step12/show_ip_route.ref.diff
@@ -0,0 +1,144 @@
+--- a/rt4/step11/show_ip_route.ref
++++ b/rt4/step12/show_ip_route.ref
+@@ -160,23 +160,13 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+- 0
++ 0,
++ 1
+ ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.7.6",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt6",
+- "active":true,
+- "labels":[
+- 16500
+- ]
+- }
+ ]
+ }
+ ],
+@@ -196,24 +186,10 @@
+ "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":[
+- 30060
+- ]
+- }
+ ]
+ }
+ ],
+@@ -352,19 +328,12 @@
+ "active":true,
+ "backupIndex":[
+ 0,
+- 1,
+- 2
++ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+- "ip":"10.0.7.6",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt6",
+- "active":true
+- },
+- {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+@@ -397,19 +366,12 @@
+ "active":true,
+ "backupIndex":[
+ 0,
+- 1,
+- 2
++ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+- "ip":"10.0.7.6",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt6",
+- "active":true
+- },
+- {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+@@ -439,14 +401,6 @@
+ 0
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.7.6",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt6",
+- "active":true
+- }
+ ]
+ }
+ ],
+@@ -460,18 +414,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"
+ }
+ ]
+ }
+@@ -492,13 +435,6 @@
+ "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/step12/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step12/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..37e3185
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step12/show_ipv6_route.ref.diff
@@ -0,0 +1,50 @@
+--- a/rt4/step11/show_ipv6_route.ref
++++ b/rt4/step12/show_ipv6_route.ref
+@@ -149,23 +149,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true,
+- "labels":[
+- 16501
+- ]
+- }
+ ]
+ }
+ ],
+@@ -184,23 +171,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 30061
+- ]
+- }
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step12/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step12/show_mpls_table.ref.diff
new file mode 100644
index 0000000..186291a
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step12/show_mpls_table.ref.diff
@@ -0,0 +1,78 @@
+--- a/rt4/step11/show_mpls_table.ref
++++ b/rt4/step12/show_mpls_table.ref
+@@ -179,17 +179,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.7.6",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":30060,
+- "nexthop":"10.0.6.5"
++ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+@@ -201,17 +191,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt6",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":30061,
+- "interface":"eth-rt5"
++ "interface":"eth-rt6"
+ }
+ ]
+ },
+@@ -223,17 +203,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.6.5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16500,
+- "nexthop":"10.0.7.6"
++ "nexthop":"10.0.6.5"
+ }
+ ]
+ },
+@@ -245,17 +215,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16501,
+- "interface":"eth-rt6"
++ "interface":"eth-rt5"
+ }
+ ]
+ }
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..644885e
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname rt4
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+! debug zebra rib detail
+!
+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/bfdd.conf b/tests/topotests/isis_tilfa_topo1/rt5/bfdd.conf
new file mode 100644
index 0000000..d27625f
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/bfdd.conf
@@ -0,0 +1,14 @@
+hostname rt5
+!
+#debug bfd network
+#debug bfd peer
+#debug bfd zebra
+!
+bfd
+ peer 10.0.8.6 interface eth-rt6
+ detect-multiplier 3
+ receive-interval 300
+ transmit-interval 300
+ no shutdown
+ !
+! \ No newline at end of file
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..3678ff9
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/isisd.conf
@@ -0,0 +1,58 @@
+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
+! debug isis ti-lfa
+!
+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-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute ti-lfa
+!
+interface eth-rt3-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute ti-lfa
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute ti-lfa
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..f40b0d3
--- /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": 10,
+ "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": 10,
+ "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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step10/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step10/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step10/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step10/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step10/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step10/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step10/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step10/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step10/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step11/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step11/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step11/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step11/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step11/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step11/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step11/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step11/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step11/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step12/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step12/show_ip_route.ref.diff
new file mode 100644
index 0000000..3d21c04
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step12/show_ip_route.ref.diff
@@ -0,0 +1,151 @@
+--- a/rt5/step11/show_ip_route.ref
++++ b/rt5/step12/show_ip_route.ref
+@@ -159,24 +159,10 @@
+ "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
+- ]
+- }
+ ]
+ }
+ ],
+@@ -187,25 +173,11 @@
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+- "metric":20,
++ "metric":30,
+ "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",
+@@ -276,19 +248,12 @@
+ "active":true,
+ "backupIndex":[
+ 0,
+- 1,
+- 2
++ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+- "ip":"10.0.8.6",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt6",
+- "active":true
+- },
+- {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+@@ -321,19 +286,12 @@
+ "active":true,
+ "backupIndex":[
+ 0,
+- 1,
+- 2
++ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+- "ip":"10.0.8.6",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt6",
+- "active":true
+- },
+- {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+@@ -439,14 +397,6 @@
+ 0
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.8.6",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt6",
+- "active":true
+- }
+ ]
+ }
+ ],
+@@ -465,39 +415,6 @@
+ "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/step12/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step12/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..66a9dac
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step12/show_ipv6_route.ref.diff
@@ -0,0 +1,53 @@
+--- a/rt5/step11/show_ipv6_route.ref
++++ b/rt5/step12/show_ipv6_route.ref
+@@ -149,23 +149,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true,
+- "labels":[
+- 16041
+- ]
+- }
+ ]
+ }
+ ],
+@@ -176,25 +163,12 @@
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+- "metric":20,
++ "metric":30,
+ "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":[
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step12/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step12/show_mpls_table.ref.diff
new file mode 100644
index 0000000..cdfc407
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step12/show_mpls_table.ref.diff
@@ -0,0 +1,80 @@
+--- a/rt5/step11/show_mpls_table.ref
++++ b/rt5/step12/show_mpls_table.ref
+@@ -179,17 +179,7 @@
+ "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"
++ "nexthop":"10.0.6.4"
+ }
+ ]
+ },
+@@ -201,17 +191,7 @@
+ "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-rt4"
+ }
+ ]
+ },
+@@ -221,18 +201,8 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.8.6",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+ "outLabel":16060,
++ "installed":true,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+@@ -243,18 +213,8 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt6",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+ "outLabel":16061,
++ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
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..424c85e
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname rt5
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+! debug zebra rib detail
+!
+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/bfdd.conf b/tests/topotests/isis_tilfa_topo1/rt6/bfdd.conf
new file mode 100644
index 0000000..0c8ba72
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/bfdd.conf
@@ -0,0 +1,14 @@
+hostname rt6
+!
+#debug bfd network
+#debug bfd peer
+#debug bfd zebra
+!
+bfd
+ peer 10.0.8.5 interface eth-rt5
+ detect-multiplier 3
+ receive-interval 300
+ transmit-interval 300
+ no shutdown
+ !
+! \ No newline at end of file
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..9d3b464
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/isisd.conf
@@ -0,0 +1,42 @@
+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
+! debug isis ti-lfa
+!
+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-interval 1
+ isis hello-multiplier 10
+ isis fast-reroute ti-lfa
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-interval 1
+ isis hello-multiplier 10
+ 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..8300ca0
--- /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": 10,
+ "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": 10,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step10/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step10/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step10/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step10/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step10/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step10/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step10/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step10/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step10/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step11/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step11/show_ip_route.ref.diff
new file mode 100644
index 0000000..e477e87
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step11/show_ip_route.ref.diff
@@ -0,0 +1,125 @@
+--- a/rt6/step10/show_ip_route.ref
++++ b/rt6/step11/show_ip_route.ref
+@@ -76,25 +76,11 @@
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+- "metric":30,
++ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+- "ip":"10.0.8.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 30030
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+@@ -150,25 +136,11 @@
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+- "metric":20,
++ "metric":30,
+ "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",
+@@ -276,22 +248,11 @@
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+- "metric":20,
++ "metric":30,
+ "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",
+@@ -307,22 +268,11 @@
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+- "metric":20,
++ "metric":30,
+ "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",
+@@ -389,19 +339,9 @@
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+- "metric":20,
++ "metric":30,
+ "nexthops":[
+ {
+- "ip":"10.0.8.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step11/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step11/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..12e0b59
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step11/show_ipv6_route.ref.diff
@@ -0,0 +1,56 @@
+--- a/rt6/step10/show_ipv6_route.ref
++++ b/rt6/step11/show_ipv6_route.ref
+@@ -72,25 +72,12 @@
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+- "metric":30,
++ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 30031
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+@@ -142,25 +129,12 @@
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+- "metric":20,
++ "metric":30,
+ "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":[
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step11/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step11/show_mpls_table.ref.diff
new file mode 100644
index 0000000..387dcca
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step11/show_mpls_table.ref.diff
@@ -0,0 +1,106 @@
+--- a/rt6/step10/show_mpls_table.ref
++++ b/rt6/step11/show_mpls_table.ref
+@@ -8,12 +8,6 @@
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":30010,
+- "installed":true,
+- "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+@@ -26,12 +20,6 @@
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":30011,
+- "installed":true,
+- "interface":"eth-rt5"
+ }
+ ]
+ },
+@@ -85,18 +73,8 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":30030,
+- "installed":true,
+- "nexthop":"10.0.8.5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+ "outLabel":16030,
++ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+@@ -107,17 +85,6 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":30031,
+- "installed":true,
+- "interface":"eth-rt5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "interface":"eth-rt4"
+ }
+@@ -173,18 +140,8 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.8.5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+ "outLabel":16500,
++ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+@@ -195,18 +152,8 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+ "outLabel":16501,
++ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step12/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step12/show_ip_route.ref.diff
new file mode 100644
index 0000000..1086b6e
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step12/show_ip_route.ref.diff
@@ -0,0 +1,153 @@
+--- a/rt6/step12/show_ip_route.ref
++++ b/rt6/step12/show_ip_route.ref
+@@ -18,16 +18,6 @@
+ "labels":[
+ 16010
+ ]
+- },
+- {
+- "fib":true,
+- "ip":"10.0.8.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 30010
+- ]
+ }
+ ]
+ }
+@@ -48,24 +38,10 @@
+ "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":[
+- 30020
+- ]
+- }
+ ]
+ }
+ ],
+@@ -108,24 +84,10 @@
+ "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":[
+- 30040
+- ]
+- }
+ ]
+ }
+ ],
+@@ -168,13 +130,6 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+- },
+- {
+- "fib":true,
+- "ip":"10.0.8.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5",
+- "active":true
+ }
+ ]
+ }
+@@ -194,17 +149,6 @@
+ "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
+ }
+ ]
+@@ -225,17 +169,6 @@
+ "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
+ }
+ ]
+@@ -297,13 +230,6 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+- },
+- {
+- "fib":true,
+- "ip":"10.0.8.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5",
+- "active":true
+ }
+ ]
+ }
+@@ -318,18 +244,7 @@
+ {
+ "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
++ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step12/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step12/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..571c66f
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step12/show_ipv6_route.ref.diff
@@ -0,0 +1,66 @@
+--- a/rt6/step12/show_ipv6_route.ref
++++ b/rt6/step12/show_ipv6_route.ref
+@@ -17,15 +17,6 @@
+ "labels":[
+ 16011
+ ]
+- },
+- {
+- "fib":true,
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 30011
+- ]
+ }
+ ]
+ }
+@@ -45,23 +36,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 16021
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 30021
+- ]
+- }
+ ]
+ }
+ ],
+@@ -102,23 +80,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 30041
+- ]
+- }
+ ]
+ }
+ ],
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step12/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step12/show_mpls_table.ref.diff
new file mode 100644
index 0000000..18322f1
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step12/show_mpls_table.ref.diff
@@ -0,0 +1,78 @@
+--- a/rt6/step12/show_mpls_table.ref
++++ b/rt6/step12/show_mpls_table.ref
+@@ -31,17 +31,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+- "nexthop":"10.0.7.4",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":30020,
+- "nexthop":"10.0.8.5"
++ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+@@ -53,17 +43,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+- "interface":"eth-rt4",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":30021,
+- "interface":"eth-rt5"
++ "interface":"eth-rt4"
+ }
+ ]
+ },
+@@ -98,17 +78,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.7.4",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":30040,
+- "nexthop":"10.0.8.5"
++ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+@@ -120,17 +90,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt4",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":30041,
+- "interface":"eth-rt5"
++ "interface":"eth-rt4"
+ }
+ ]
+ }, \ No newline at end of file
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..94fed46
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/zebra.conf
@@ -0,0 +1,23 @@
+log file zebra.log
+!
+hostname rt6
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+! debug zebra rib detail
+!
+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..e2bbf45
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py
@@ -0,0 +1,1113 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_isis_tilfa_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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, 12 + 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_BFD, os.path.join(CWD, "/dev/null".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=120, wait=0.5):
+ "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=count, wait=wait)
+ 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"]
+ )
+
+
+#
+# Step 10
+#
+# Action(s):
+# - Setting spf-delay-ietf init-delay of 15s
+#
+# Expected changes:
+# - No routing table change
+# - At the end of test, SPF reacts to a failure in 15s
+#
+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("Setting spf-delay-ietf init-delay of 15s")
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "spf-delay-ietf init-delay 15000 short-delay 0 long-delay 0 holddown 0 time-to-learn 0"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ 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", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][10]["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", outputs[rname][10]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 11
+#
+# Action(s):
+# - shut the eth-rt5 interface on rt6
+#
+# Expected changes:
+# - Route switchover of routes via eth-rt5
+#
+def test_rt6_step11():
+ logger.info(
+ "Test (step 11): Check IPv4/6 RIB and MPLS table after a LFA switchover"
+ )
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Shut a rt6 interface to rt5 from the switch side and check fast-reroute"
+ )
+ tgen.net.cmd_raises("ip link set %s down" % tgen.net["s8"].intfs[1])
+
+ rname = "rt6"
+ router_compare_json_output(
+ rname,
+ "show ip route isis json",
+ outputs[rname][11]["show_ip_route.ref"],
+ count=20,
+ )
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][11]["show_ipv6_route.ref"],
+ count=20,
+ )
+ router_compare_json_output(
+ rname,
+ "show mpls table json",
+ outputs[rname][11]["show_mpls_table.ref"],
+ count=20,
+ )
+
+
+#
+# Step 12
+#
+# Action(s): wait for the convergence and SPF computation on rt6
+#
+# Expected changes:
+# - convergence of IPv4/6 RIB and MPLS table
+#
+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)
+
+ logger.info("Check SPF convergence")
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show ip route isis json",
+ outputs[rname][12]["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",
+ outputs[rname][12]["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",
+ outputs[rname][12]["show_mpls_table.ref"],
+ )
+
+
+#
+# Step 13
+#
+# Action(s):
+# - unshut the rt6 to rt5 interface
+# - Setup BFD
+#
+# Expected changes:
+# - All route tables go back to previous state situation
+# - At the end of test, next SPF is scheduled in approximatively 15s
+#
+def test_rib_ipv4_step13():
+ logger.info("Test (step 13): 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("Unsetting spf-delay-ietf init-delay of 15s")
+ tgen.net["rt6"].cmd('vtysh -c "conf t" -c "router isis 1" -c "no spf-delay-ietf"')
+
+ logger.info(
+ "Unshut the rt6 interface to rt5 from the switch side and check fast-reroute"
+ )
+ tgen.net.cmd_raises("ip link set %s up" % tgen.net["s8"].intfs[1])
+
+ logger.info("Setup BFD on rt5 and rt6")
+ for rname in ["rt5", "rt6"]:
+ conf_file = os.path.join(CWD, "{}/bfdd.conf".format(rname))
+ tgen.net[rname].cmd("vtysh -f {}".format(conf_file))
+
+ expect = (
+ '[{"multihop":false,"peer":"10.0.8.5","interface":"eth-rt5","status":"up"}]'
+ )
+ router_compare_json_output("rt6", "show bfd peers json", expect)
+
+ # Unset link detection. We want zebra to consider linkdow as operationaly up
+ # in order that BFD triggers LFA instead of the interface down
+
+ # reset spf-interval
+ logger.info("Set spf-interval to 15s")
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "spf-delay-ietf init-delay 15000 short-delay 0 long-delay 0 holddown 0 time-to-learn 0"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][10]["show_ip_route.ref"]
+ )
+
+ logger.info("Set ISIS BFD")
+ tgen.net["rt5"].cmd('vtysh -c "conf t" -c "int eth-rt6" -c "isis bfd"')
+ tgen.net["rt6"].cmd('vtysh -c "conf t" -c "int eth-rt5" -c "isis bfd"')
+
+ expect = (
+ '[{"multihop":false,"peer":"10.0.8.5","interface":"eth-rt5","status":"up"}]'
+ )
+ router_compare_json_output(
+ rname,
+ "show bfd peers json",
+ expect,
+ )
+
+
+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)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][10]["show_ipv6_route.ref"],
+ )
+
+
+def test_mpls_lib_step13():
+ logger.info("Test (step 13): 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][10]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 14
+#
+# Action(s):
+# - drop traffic between rt5 and rt6 by shutting down the bridge between
+# the routers. Interfaces on rt5 and rt6 stay up.
+#
+# Expected changes:
+# - Route switchover of routes via eth-rt5
+#
+def test_rt6_step14():
+ logger.info("Test (step 14): verify IPv4/6 RIB and MPLS table")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Drop traffic between rt5 and rt6")
+ tgen.net.cmd_raises("ip link set s8 down")
+
+ rname = "rt6"
+
+ expect = (
+ '[{"multihop":false,"peer":"10.0.8.5","interface":"eth-rt5","status":"down"}]'
+ )
+ router_compare_json_output(
+ rname,
+ "show bfd peers json",
+ expect,
+ count=40,
+ wait=0.5,
+ )
+
+ router_compare_json_output(
+ rname,
+ "show ip route isis json",
+ outputs[rname][11]["show_ip_route.ref"],
+ count=20,
+ )
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][11]["show_ipv6_route.ref"],
+ count=20,
+ wait=2,
+ )
+ router_compare_json_output(
+ rname,
+ "show mpls table json",
+ outputs[rname][11]["show_mpls_table.ref"],
+ count=20,
+ )
+
+
+#
+# Step 15
+#
+# Action(s): wait for the convergence and SPF computation on rt6
+#
+# Expected changes:
+# - convergence of IPv4/6 RIB and MPLS table
+#
+def test_rib_ipv4_step15():
+ logger.info("Test (step 15): 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("Check SPF convergence")
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show ip route isis json",
+ outputs[rname][12]["show_ip_route.ref"],
+ )
+
+
+def test_rib_ipv6_step15():
+ logger.info("Test (step 15): 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][12]["show_ipv6_route.ref"],
+ )
+
+
+def test_mpls_lib_step15():
+ logger.info("Test (step 15): 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][12]["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..c3b6e3b
--- /dev/null
+++ b/tests/topotests/isis_topo1/r3/r3_route.json
@@ -0,0 +1,165 @@
+{
+ "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
+ }
+ ],
+ "192.0.2.6/32": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "fib": true,
+ "ip": "10.0.10.1",
+ "interfaceName": "r3-eth1"
+ }
+ ],
+ "prefix": "192.0.2.6/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..8fd46ad
--- /dev/null
+++ b/tests/topotests/isis_topo1/r4/r4_route.json
@@ -0,0 +1,80 @@
+{
+ "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
+ }
+ ],
+ "192.0.2.6/32": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "fib": true,
+ "ip": "10.0.11.1",
+ "interfaceName": "r4-eth1"
+ }
+ ],
+ "prefix": "192.0.2.6/32",
+ "protocol": "isis",
+ "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..81686e0
--- /dev/null
+++ b/tests/topotests/isis_topo1/r5/isisd.conf
@@ -0,0 +1,24 @@
+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
+ redistribute ipv4 table 20 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..ec544b8
--- /dev/null
+++ b/tests/topotests/isis_topo1/r5/r5_route.json
@@ -0,0 +1,161 @@
+{
+ "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
+ }
+ ],
+ "192.0.2.6/32": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "fib": true,
+ "ip": "10.0.10.6",
+ "interfaceName": "r5-eth0"
+ }
+ ],
+ "prefix": "192.0.2.6/32",
+ "protocol": "table",
+ "instance": 20,
+ "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..7c400f7
--- /dev/null
+++ b/tests/topotests/isis_topo1/r5/zebra.conf
@@ -0,0 +1,15 @@
+hostname r5
+ip route 192.0.2.6/32 10.0.10.6 table 20
+ip import 20
+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..48e9d53
--- /dev/null
+++ b/tests/topotests/isis_topo1/test_isis_topo1.py
@@ -0,0 +1,896 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_isis_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+test_isis_topo1.py: Test ISIS topology.
+"""
+import datetime
+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.common_config import (
+ retry,
+ stop_router,
+ start_router,
+)
+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_isis_overload_on_startup():
+ "Check that overload on startup behaves as expected"
+
+ tgen = get_topogen()
+ net = get_topogen().net
+ overload_time = 120
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Testing overload on startup behavior")
+
+ # Configure set-overload-bit on-startup on r3
+ r3 = tgen.gears["r3"]
+ r3.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ set-overload-bit on-startup {overload_time}
+ """
+ )
+ # Restart r3
+ logger.info("Stop router")
+ stop_router(tgen, "r3")
+ logger.info("Start router")
+
+ tstamp_before_start_router = datetime.datetime.now()
+ start_router(tgen, "r3")
+ tstamp_after_start_router = datetime.datetime.now()
+ startup_router_time = (
+ tstamp_after_start_router - tstamp_before_start_router
+ ).total_seconds()
+
+ # Check that the overload bit is set in r3's LSP
+ check_lsp_overload_bit("r3", "r3.00-00", "0/0/1")
+ check_lsp_overload_bit("r1", "r3.00-00", "0/0/1")
+
+ # Attempt to unset overload bit while timer is still running
+ r3.vtysh_cmd(
+ """
+ configure
+ router isis 1
+ no set-overload-bit on-startup
+ no set-overload-bit
+ """
+ )
+
+ # Check overload bit is still set
+ check_lsp_overload_bit("r1", "r3.00-00", "0/0/1")
+
+ # Check that overload bit is unset after timer completes
+ check_lsp_overload_bit("r3", "r3.00-00", "0/0/0")
+ tstamp_after_bit_unset = datetime.datetime.now()
+ check_lsp_overload_bit("r1", "r3.00-00", "0/0/0")
+
+ # Collect time overloaded
+ time_overloaded = (
+ tstamp_after_bit_unset - tstamp_after_start_router
+ ).total_seconds()
+ logger.info(f"Time Overloaded: {time_overloaded}")
+
+ # Use time it took to startup router as lower bound
+ logger.info(
+ f"Assert that overload time falls in range: {overload_time - startup_router_time} < {time_overloaded} <= {overload_time}"
+ )
+ result = overload_time - startup_router_time < time_overloaded <= overload_time
+ assert result
+
+
+def test_isis_overload_on_startup_cancel_timer():
+ "Check that overload on startup timer is cancelled when overload bit is set/unset"
+
+ tgen = get_topogen()
+ net = get_topogen().net
+ overload_time = 90
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Testing overload on startup behavior with set overload bit: cancel timer"
+ )
+
+ # Configure set-overload-bit on-startup on r3
+ r3 = tgen.gears["r3"]
+ r3.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ set-overload-bit on-startup {overload_time}
+ set-overload-bit
+ """
+ )
+ # Restart r3
+ logger.info("Stop router")
+ stop_router(tgen, "r3")
+ logger.info("Start router")
+ start_router(tgen, "r3")
+
+ # Check that the overload bit is set in r3's LSP
+ check_lsp_overload_bit("r3", "r3.00-00", "0/0/1")
+
+ # Check that overload timer is running
+ check_overload_timer("r3", True)
+
+ # Unset overload bit while timer is running
+ r3.vtysh_cmd(
+ """
+ configure
+ router isis 1
+ no set-overload-bit
+ """
+ )
+
+ # Check that overload timer is cancelled
+ check_overload_timer("r3", False)
+
+ # Check overload bit is unset
+ check_lsp_overload_bit("r3", "r3.00-00", "0/0/0")
+
+
+def test_isis_overload_on_startup_override_timer():
+ "Check that overload bit remains set after overload timer expires if overload bit is configured"
+
+ tgen = get_topogen()
+ net = get_topogen().net
+ overload_time = 60
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Testing overload on startup behavior with set overload bit: override timer"
+ )
+
+ # Configure set-overload-bit on-startup on r3
+ r3 = tgen.gears["r3"]
+ r3.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ set-overload-bit on-startup {overload_time}
+ set-overload-bit
+ """
+ )
+ # Restart r3
+ logger.info("Stop router")
+ stop_router(tgen, "r3")
+ logger.info("Start router")
+ start_router(tgen, "r3")
+
+ # Check that the overload bit is set in r3's LSP
+ check_lsp_overload_bit("r3", "r3.00-00", "0/0/1")
+
+ # Check that overload timer is running
+ check_overload_timer("r3", True)
+
+ # Check that overload timer expired
+ check_overload_timer("r3", False)
+
+ # Check overload bit is still set
+ check_lsp_overload_bit("r3", "r3.00-00", "0/0/1")
+
+
+def test_isis_advertise_passive_only():
+ """Check that we only advertise prefixes of passive interfaces when advertise-passive-only is enabled."""
+ tgen = get_topogen()
+ net = get_topogen().net
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Testing isis advertise-passive-only behavior")
+ expected_prefixes_no_advertise_passive_only = set(
+ ["10.0.20.0/24", "10.254.0.1/32", "2001:db8:f::1/128", "2001:db8:1:1::/64"]
+ )
+ expected_prefixes_advertise_passive_only = set(
+ ["10.254.0.1/32", "2001:db8:f::1/128"]
+ )
+ lsp_id = "r1.00-00"
+
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd(
+ """
+ configure
+ router isis 1
+ no redistribute ipv4 connected level-2
+ no redistribute ipv6 connected level-2
+ interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+ end
+ """
+ )
+
+ result = check_advertised_prefixes(
+ r1, lsp_id, expected_prefixes_no_advertise_passive_only
+ )
+ assert result is True, result
+
+ r1.vtysh_cmd(
+ """
+ configure
+ router isis 1
+ advertise-passive-only
+ end
+ """
+ )
+
+ result = check_advertised_prefixes(
+ r1, lsp_id, expected_prefixes_advertise_passive_only
+ )
+ assert result is True, result
+
+
+def test_isis_hello_padding_during_adjacency_formation():
+ """Check that IIH packets is only padded when adjacency is still being formed
+ when isis hello padding during-adjacency-formation is configured
+ """
+ tgen = get_topogen()
+ net = get_topogen().net
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Testing isis hello padding during-adjacency-formation behavior")
+ r3 = tgen.gears["r3"]
+
+ # Reduce hello-multiplier to make the adjacency go down faster.
+ r3.vtysh_cmd(
+ """
+ configure
+ interface r3-eth0
+ isis hello-multiplier 2
+ """
+ )
+
+ r1 = tgen.gears["r1"]
+ cmd_output = r1.vtysh_cmd(
+ """
+ configure
+ interface r1-eth0
+ isis hello padding during-adjacency-formation
+ end
+ debug isis adj-packets
+ """
+ )
+ result = check_last_iih_packet_for_padding(r1, expect_padding=False)
+ assert result is True, result
+
+ r3.vtysh_cmd(
+ """
+ configure
+ interface r3-eth0
+ shutdown
+ """
+ )
+ result = check_last_iih_packet_for_padding(r1, expect_padding=True)
+ assert result is True, result
+
+ r3 = tgen.gears["r3"]
+ r3.vtysh_cmd(
+ """
+ configure
+ interface r3-eth0
+ no shutdown
+ """
+ )
+ result = check_last_iih_packet_for_padding(r1, expect_padding=False)
+ assert result is True, result
+
+
+@retry(retry_timeout=5)
+def check_last_iih_packet_for_padding(router, expect_padding):
+ logfilename = "{}/{}".format(router.gearlogdir, "isisd.log")
+ last_hello_packet_line = None
+ with open(logfilename, "r") as f:
+ lines = f.readlines()
+ for line in lines:
+ if re.search("Sending .+? IIH", line):
+ last_hello_packet_line = line
+
+ if last_hello_packet_line is None:
+ return "Expected IIH packet in {}, but no packet found".format(logfilename)
+
+ interface_name, packet_length = re.search(
+ r"Sending .+ IIH on (.+), length (\d+)", last_hello_packet_line
+ ).group(1, 2)
+ packet_length = int(packet_length)
+ interface_output = router.vtysh_cmd("show interface {} json".format(interface_name))
+ interface_json = json.loads(interface_output)
+ padded_packet_length = interface_json[interface_name]["mtu"] - 3
+ if expect_padding:
+ if packet_length == padded_packet_length:
+ return True
+ return (
+ "Expected padded packet with length {}, got packet with length {}".format(
+ padded_packet_length, packet_length
+ )
+ )
+ if packet_length < padded_packet_length:
+ return True
+ return "Expected unpadded packet with length less than {}, got packet with length {}".format(
+ padded_packet_length, packet_length
+ )
+
+
+@retry(retry_timeout=5)
+def check_advertised_prefixes(router, lsp_id, expected_prefixes):
+ output = router.vtysh_cmd("show isis database detail {}".format(lsp_id))
+ prefixes = set(re.findall(r"IP(?:v6)? Reachability: (.*) \(Metric: 10\)", output))
+ if prefixes == expected_prefixes:
+ return True
+ return str({"expected_prefixes:": expected_prefixes, "prefixes": prefixes})
+
+
+@retry(retry_timeout=200)
+def _check_lsp_overload_bit(router, overloaded_router_lsp, att_p_ol_expected):
+ "Verfiy overload bit in router's LSP"
+
+ tgen = get_topogen()
+ router = tgen.gears[router]
+ logger.info(f"check_overload_bit {router}")
+ isis_database_output = router.vtysh_cmd(
+ "show isis database {} json".format(overloaded_router_lsp)
+ )
+
+ database_json = json.loads(isis_database_output)
+ att_p_ol = database_json["areas"][0]["levels"][1]["att-p-ol"]
+ if att_p_ol == att_p_ol_expected:
+ return True
+ return "{} peer with expected att_p_ol {} got {} ".format(
+ router.name, att_p_ol_expected, att_p_ol
+ )
+
+
+def check_lsp_overload_bit(router, overloaded_router_lsp, att_p_ol_expected):
+ "Verfiy overload bit in router's LSP"
+
+ assertmsg = _check_lsp_overload_bit(
+ router, overloaded_router_lsp, att_p_ol_expected
+ )
+ assert assertmsg is True, assertmsg
+
+
+@retry(retry_timeout=200)
+def _check_overload_timer(router, timer_expected):
+ "Verfiy overload bit in router's LSP"
+
+ tgen = get_topogen()
+ router = tgen.gears[router]
+ thread_output = router.vtysh_cmd("show thread timers")
+
+ timer_running = "set_overload_on_start_timer" in thread_output
+ if timer_running == timer_expected:
+ return True
+ return "Expected timer running status: {}".format(timer_expected)
+
+
+def check_overload_timer(router, timer_expected):
+ "Verfiy overload bit in router's LSP"
+
+ assertmsg = _check_overload_timer(router, timer_expected)
+ assert assertmsg is True, 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..032319c
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.py
@@ -0,0 +1,428 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by Niral Networks, Inc. ("Niral Networks")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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/kinds.yaml b/tests/topotests/kinds.yaml
new file mode 100644
index 0000000..127790e
--- /dev/null
+++ b/tests/topotests/kinds.yaml
@@ -0,0 +1,30 @@
+version: 1
+kinds:
+ - name: frr
+ cmd: |
+ chown frr:frr -R /var/run/frr
+ chown frr:frr -R /var/log/frr
+ /usr/lib/frr/frrinit.sh start
+ tail -F /var/log/frr/frr.log
+ cleanup-cmd: |
+ /usr/lib/frr/frrinit.sh stop
+ volumes:
+ - "./%NAME%:/etc/frr"
+ - "%RUNDIR%/var.log.frr:/var/log/frr"
+ - "%RUNDIR%/var.run.frr:/var/run/frr"
+ cap-add:
+ - SYS_ADMIN
+ - AUDIT_WRITE
+ merge: ["volumes"]
+cli:
+ commands:
+ - name: ""
+ exec: "vtysh -c '{}'"
+ format: "[ROUTER ...] COMMAND"
+ help: "execute vtysh COMMAND on the router[s]"
+ kinds: ["frr"]
+ - name: "vtysh"
+ exec: "/usr/bin/vtysh"
+ format: "vtysh ROUTER [ROUTER ...]"
+ new-window: true
+ kinds: ["frr"]
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..c1c231d
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json
@@ -0,0 +1,12 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "nbrPriority":2,
+ "converged":"Full",
+ "ifaceAddress":"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..ee69af5
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json
@@ -0,0 +1,28 @@
+{
+ "neighbors":{
+ "1.1.1.1":[
+ {
+ "nbrPriority":1,
+ "converged":"Full",
+ "ifaceAddress":"10.0.1.1",
+ "ifaceName":"r2-eth0:10.0.1.2"
+ }
+ ],
+ "3.3.3.3":[
+ {
+ "nbrPriority":2,
+ "converged":"Full",
+ "ifaceAddress":"10.0.2.3",
+ "ifaceName":"r2-eth1:10.0.2.2"
+ }
+ ],
+ "4.4.4.4":[
+ {
+ "nbrPriority":3,
+ "converged":"Full",
+ "ifaceAddress":"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..3f76542
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json
@@ -0,0 +1,20 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "nbrPriority":1,
+ "converged":"Full",
+ "ifaceAddress":"10.0.2.2",
+ "ifaceName":"r3-eth0:10.0.2.3"
+ }
+ ],
+ "4.4.4.4":[
+ {
+ "nbrPriority":3,
+ "converged":"Full",
+ "ifaceAddress":"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..5395cd2
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json
@@ -0,0 +1,21 @@
+
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "nbrPriority":1,
+ "converged":"Full",
+ "ifaceAddress":"10.0.2.2",
+ "ifaceName":"r4-eth0:10.0.2.4"
+ }
+ ],
+ "3.3.3.3":[
+ {
+ "nbrPriority":2,
+ "converged":"Full",
+ "ifaceAddress":"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..c2bcaa8
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/test_ldp_oc_acl_topo1.py
@@ -0,0 +1,245 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ldp_oc_acl_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+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..c1c231d
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json
@@ -0,0 +1,12 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "nbrPriority":2,
+ "converged":"Full",
+ "ifaceAddress":"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..ee69af5
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json
@@ -0,0 +1,28 @@
+{
+ "neighbors":{
+ "1.1.1.1":[
+ {
+ "nbrPriority":1,
+ "converged":"Full",
+ "ifaceAddress":"10.0.1.1",
+ "ifaceName":"r2-eth0:10.0.1.2"
+ }
+ ],
+ "3.3.3.3":[
+ {
+ "nbrPriority":2,
+ "converged":"Full",
+ "ifaceAddress":"10.0.2.3",
+ "ifaceName":"r2-eth1:10.0.2.2"
+ }
+ ],
+ "4.4.4.4":[
+ {
+ "nbrPriority":3,
+ "converged":"Full",
+ "ifaceAddress":"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..3f76542
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json
@@ -0,0 +1,20 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "nbrPriority":1,
+ "converged":"Full",
+ "ifaceAddress":"10.0.2.2",
+ "ifaceName":"r3-eth0:10.0.2.3"
+ }
+ ],
+ "4.4.4.4":[
+ {
+ "nbrPriority":3,
+ "converged":"Full",
+ "ifaceAddress":"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..5395cd2
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json
@@ -0,0 +1,21 @@
+
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "nbrPriority":1,
+ "converged":"Full",
+ "ifaceAddress":"10.0.2.2",
+ "ifaceName":"r4-eth0:10.0.2.4"
+ }
+ ],
+ "3.3.3.3":[
+ {
+ "nbrPriority":2,
+ "converged":"Full",
+ "ifaceAddress":"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..387fd89
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/test_ldp_oc_topo1.py
@@ -0,0 +1,225 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ldp_oc_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by by Volta Networks
+#
+
+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..675af4d
--- /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..cdcd9a2
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp: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/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..045a8cf
--- /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..17ddc2a
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp: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
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..52a67d8
--- /dev/null
+++ b/tests/topotests/ldp_snmp/test_ldp_snmp_topo1.py
@@ -0,0 +1,372 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ldp_isis_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+"""
+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..675af4d
--- /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..045a8cf
--- /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..cb6b784
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.py
@@ -0,0 +1,613 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ldp_isis_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+"""
+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..e25523d
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json
@@ -0,0 +1,26 @@
+{
+ "neighbors": {
+ "2.2.2.2": [
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 1,
+ "converged": "Full",
+ "ifaceAddress": "10.0.1.2",
+ "ifaceName": "r1-eth1:10.0.1.1",
+ "linkStateRequestListCounter": 0
+ }
+ ],
+ "3.3.3.3": [
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 1,
+ "converged": "Full",
+ "ifaceAddress": "10.0.2.3",
+ "ifaceName": "r1-eth2:10.0.2.1",
+ "linkStateRequestListCounter": 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..675af4d
--- /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..fa2ea86
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json
@@ -0,0 +1,26 @@
+{
+ "neighbors": {
+ "1.1.1.1": [
+ {
+ "nbrPriority":1,
+ "converged":"Full",
+ "ifaceAddress":"10.0.1.1",
+ "ifaceName":"r2-eth1:10.0.1.2",
+ "linkStateRetransmissionListCounter":0,
+ "linkStateRequestListCounter":0,
+ "databaseSummaryListCounter":0
+ }
+ ],
+ "3.3.3.3": [
+ {
+ "nbrPriority":1,
+ "converged":"Full",
+ "ifaceAddress":"10.0.3.3",
+ "ifaceName":"r2-eth2:10.0.3.2",
+ "linkStateRetransmissionListCounter":0,
+ "linkStateRequestListCounter":0,
+ "databaseSummaryListCounter":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..045a8cf
--- /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..bf77e08
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json
@@ -0,0 +1,26 @@
+{
+ "neighbors": {
+ "1.1.1.1": [
+ {
+ "nbrPriority":1,
+ "converged":"Full",
+ "ifaceAddress":"10.0.2.1",
+ "ifaceName":"r3-eth1:10.0.2.3",
+ "linkStateRetransmissionListCounter":0,
+ "linkStateRequestListCounter":0,
+ "databaseSummaryListCounter":0
+ }
+ ],
+ "2.2.2.2": [
+ {
+ "nbrPriority":1,
+ "converged":"Full",
+ "ifaceAddress":"10.0.3.2",
+ "ifaceName":"r3-eth2:10.0.3.3",
+ "linkStateRetransmissionListCounter":0,
+ "linkStateRequestListCounter":0,
+ "databaseSummaryListCounter":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..760b4e3
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/test_ldp_sync_ospf_topo1.py
@@ -0,0 +1,430 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ldp_ospf_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+"""
+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..f47c2df
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json
@@ -0,0 +1,14 @@
+{
+ "neighbors": {
+ "2.2.2.2": [
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 1,
+ "converged": "Full",
+ "ifaceAddress": "10.0.1.2",
+ "linkStateRequestListCounter": 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..901282f
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json
@@ -0,0 +1,42 @@
+{
+ "neighbors": {
+ "1.1.1.1": [
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 1,
+ "converged": "Full",
+ "ifaceAddress": "10.0.1.1",
+ "linkStateRequestListCounter": 0
+ }
+ ],
+ "3.3.3.3": [
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 1,
+ "converged": "Full",
+ "ifaceAddress": "10.0.2.3",
+ "linkStateRequestListCounter": 0
+ },
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 1,
+ "converged": "Full",
+ "ifaceAddress": "10.0.3.3",
+ "linkStateRequestListCounter": 0
+ }
+ ],
+ "4.4.4.4": [
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 1,
+ "converged": "Full",
+ "ifaceAddress": "10.0.2.4",
+ "linkStateRequestListCounter": 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..164040a
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json
@@ -0,0 +1,32 @@
+{
+ "neighbors": {
+ "2.2.2.2": [
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 1,
+ "converged": "Full",
+ "ifaceAddress": "10.0.2.2",
+ "linkStateRequestListCounter": 0
+ },
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 1,
+ "converged": "Full",
+ "ifaceAddress": "10.0.3.2",
+ "linkStateRequestListCounter": 0
+ }
+ ],
+ "4.4.4.4": [
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 1,
+ "converged": "Full",
+ "ifaceAddress": "10.0.2.4",
+ "linkStateRequestListCounter": 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..98c759a
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json
@@ -0,0 +1,24 @@
+{
+ "neighbors": {
+ "2.2.2.2": [
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 1,
+ "converged": "Full",
+ "ifaceAddress": "10.0.2.2",
+ "linkStateRequestListCounter": 0
+ }
+ ],
+ "3.3.3.3": [
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 1,
+ "converged": "Full",
+ "ifaceAddress": "10.0.2.3",
+ "linkStateRequestListCounter": 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..fdd7558
--- /dev/null
+++ b/tests/topotests/ldp_topo1/test_ldp_topo1.py
@@ -0,0 +1,844 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_bgp_multiview_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2016 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+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..9acb4f7
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json
@@ -0,0 +1,26 @@
+{
+ "neighbors": {
+ "2.2.2.2": [
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 2,
+ "converged": "Full",
+ "ifaceAddress": "10.0.1.2",
+ "ifaceName": "r1-eth1:10.0.1.1",
+ "linkStateRequestListCounter": 0
+ }
+ ],
+ "3.3.3.3": [
+ {
+ "databaseSummaryListCounter": 0,
+ "linkStateRetransmissionListCounter": 0,
+ "nbrPriority": 2,
+ "converged": "Full",
+ "ifaceAddress": "10.0.2.3",
+ "ifaceName": "r1-eth2:10.0.2.1",
+ "linkStateRequestListCounter": 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..675af4d
--- /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..6634199
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json
@@ -0,0 +1,26 @@
+{
+ "neighbors": {
+ "1.1.1.1": [
+ {
+ "nbrPriority":1,
+ "converged":"Full",
+ "ifaceAddress":"10.0.1.1",
+ "ifaceName":"r2-eth1:10.0.1.2",
+ "linkStateRetransmissionListCounter":0,
+ "linkStateRequestListCounter":0,
+ "databaseSummaryListCounter":0
+ }
+ ],
+ "3.3.3.3": [
+ {
+ "nbrPriority":2,
+ "converged":"Full",
+ "ifaceAddress":"10.0.3.3",
+ "ifaceName":"r2-eth2:10.0.3.2",
+ "linkStateRetransmissionListCounter":0,
+ "linkStateRequestListCounter":0,
+ "databaseSummaryListCounter":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..045a8cf
--- /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..bf77e08
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json
@@ -0,0 +1,26 @@
+{
+ "neighbors": {
+ "1.1.1.1": [
+ {
+ "nbrPriority":1,
+ "converged":"Full",
+ "ifaceAddress":"10.0.2.1",
+ "ifaceName":"r3-eth1:10.0.2.3",
+ "linkStateRetransmissionListCounter":0,
+ "linkStateRequestListCounter":0,
+ "databaseSummaryListCounter":0
+ }
+ ],
+ "2.2.2.2": [
+ {
+ "nbrPriority":1,
+ "converged":"Full",
+ "ifaceAddress":"10.0.3.2",
+ "ifaceName":"r3-eth2:10.0.3.3",
+ "linkStateRetransmissionListCounter":0,
+ "linkStateRequestListCounter":0,
+ "databaseSummaryListCounter":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..1f4a4b5
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.py
@@ -0,0 +1,291 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ldp_vpls_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..3a16ed5
--- /dev/null
+++ b/tests/topotests/lib/bgp.py
@@ -0,0 +1,5596 @@
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+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",
+ "bgp_always_compare_med": True,
+ "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)
+
+ if "bgp_always_compare_med" in bgp_data:
+ bgp_always_compare_med = bgp_data["bgp_always_compare_med"]
+ if bgp_always_compare_med == True:
+ config_data.append("bgp always-compare-med")
+ elif bgp_always_compare_med == False:
+ config_data.append("no bgp always-compare-med")
+
+ 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)
+
+ config_data.append("exit")
+ 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():
+ remote_as = 0
+ for dest_link, peer in peer_dict["dest_link"].items():
+ local_asn = peer.setdefault("local_asn", {})
+ if local_asn:
+ local_as = local_asn.setdefault("local_as", 0)
+ remote_as = local_asn.setdefault("remote_as", 0)
+ no_prepend = local_asn.setdefault("no_prepend", False)
+ replace_as = local_asn.setdefault("replace_as", False)
+ if local_as == remote_as:
+ assert False is True, (
+ " Configuration Error : Router must not have "
+ "same AS-NUMBER as Local AS NUMBER"
+ )
+ 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"]:
+ if not remote_as:
+ remote_as = vrf_data["local_as"]
+ break
+ else:
+ if "vrf" not in vrf_data:
+ if not remote_as:
+ remote_as = vrf_data["local_as"]
+ break
+ else:
+ if not remote_as:
+ 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 local_asn and local_as:
+ cmd = "{} local-as {}".format(neigh_cxt, local_as)
+ if no_prepend:
+ cmd = "{} no-prepend".format(cmd)
+ if replace_as:
+ cmd = "{} replace-as".format(cmd)
+ config_data.append("{}".format(cmd))
+
+ 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, addr_type=None):
+ """
+ API will verify if BGP is converged with in the given time frame.
+ Running "show bgp summary json" command and verify bgp neighbor
+ state is established,
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: input json file data
+ * `dut`: device under test
+ * `addr_type` : address type for which verification to be done, by-default both v4 and v6
+
+ 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
+
+ if addr_type and addr_type != _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
+
+ if addr_type and addr_type != _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, rid=None):
+ """
+ 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
+ try:
+ bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
+ except TypeError:
+ bgp_addr_type = topo["routers"][router]["bgp"][0]["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":
+ if rid:
+ run_frr_cmd(rnode, "clear bgp ipv4 {}".format(rid))
+ else:
+ run_frr_cmd(rnode, "clear bgp ipv4 *")
+ elif addr_type == "ipv6":
+ if rid:
+ run_frr_cmd(rnode, "clear bgp ipv6 {}".format(rid))
+ else:
+ 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
+ try:
+ bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
+ except TypeError:
+ bgp_addr_type = topo["routers"][router]["bgp"][0]["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 [
+ nh
+ for nh in rib_routes_json[route][0]["nexthops"]
+ if nh["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..699c7a4
--- /dev/null
+++ b/tests/topotests/lib/bgprib.py
@@ -0,0 +1,165 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# Copyright 2018, LabN Consulting, L.L.C.
+
+#
+# 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/bmp_collector/bgp/__init__.py b/tests/topotests/lib/bmp_collector/bgp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/lib/bmp_collector/bgp/__init__.py
diff --git a/tests/topotests/lib/bmp_collector/bgp/open/__init__.py b/tests/topotests/lib/bmp_collector/bgp/open/__init__.py
new file mode 100644
index 0000000..6c814ee
--- /dev/null
+++ b/tests/topotests/lib/bmp_collector/bgp/open/__init__.py
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: ISC
+
+# Copyright 2023 6WIND S.A.
+# Authored by Farid Mihoub <farid.mihoub@6wind.com>
+#
+import ipaddress
+import struct
+
+
+class BGPOpen:
+ UNPACK_STR = '!16sHBBHH4sB'
+
+ @classmethod
+ def dissect(cls, data):
+ (marker,
+ length,
+ open_type,
+ version,
+ my_as,
+ hold_time,
+ bgp_id,
+ optional_params_len) = struct.unpack_from(cls.UNPACK_STR, data)
+
+ data = data[struct.calcsize(cls.UNPACK_STR) + optional_params_len:]
+
+ # XXX: parse optional parameters
+
+ return data, {
+ 'version': version,
+ 'my_as': my_as,
+ 'hold_time': hold_time,
+ 'bgp_id': ipaddress.ip_address(bgp_id),
+ 'optional_params_len': optional_params_len,
+ }
diff --git a/tests/topotests/lib/bmp_collector/bgp/update/__init__.py b/tests/topotests/lib/bmp_collector/bgp/update/__init__.py
new file mode 100644
index 0000000..d079b35
--- /dev/null
+++ b/tests/topotests/lib/bmp_collector/bgp/update/__init__.py
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: ISC
+
+# Copyright 2023 6WIND S.A.
+# Authored by Farid Mihoub <farid.mihoub@6wind.com>
+#
+import ipaddress
+import struct
+
+from .nlri import NlriIPv4Unicast
+from .path_attributes import PathAttribute
+
+
+#------------------------------------------------------------------------------
+class BGPUpdate:
+ UNPACK_STR = '!16sHBH'
+ STATIC_SIZE = 23
+
+ @classmethod
+ def dissect(cls, data):
+ msg = {'bmp_log_type': 'update'}
+ common_size = struct.calcsize(cls.UNPACK_STR)
+ (marker,
+ length,
+ update_type,
+ withdrawn_routes_len) = struct.unpack_from(cls.UNPACK_STR, data)
+
+ # get withdrawn routes
+ withdrawn_routes = ''
+ if withdrawn_routes_len:
+ withdrawn_routes = NlriIPv4Unicast.parse(
+ data[common_size:common_size + withdrawn_routes_len]
+ )
+ msg['bmp_log_type'] = 'withdraw'
+ msg.update(withdrawn_routes)
+
+ # get path attributes
+ (total_path_attrs_len,) = struct.unpack_from(
+ '!H', data[common_size+withdrawn_routes_len:])
+
+ if total_path_attrs_len:
+ offset = cls.STATIC_SIZE + withdrawn_routes_len
+ path_attrs_data = data[offset:offset + total_path_attrs_len]
+ while path_attrs_data:
+ path_attrs_data, pattr = PathAttribute.dissect(path_attrs_data)
+ if pattr:
+ msg = {**msg, **pattr}
+
+ # get nlri
+ nlri_len = length - cls.STATIC_SIZE - withdrawn_routes_len - total_path_attrs_len
+ if nlri_len > 0:
+ nlri = NlriIPv4Unicast.parse(data[length - nlri_len:length])
+ msg.update(nlri)
+
+ return data[length:], msg
diff --git a/tests/topotests/lib/bmp_collector/bgp/update/af.py b/tests/topotests/lib/bmp_collector/bgp/update/af.py
new file mode 100644
index 0000000..01af1ae
--- /dev/null
+++ b/tests/topotests/lib/bmp_collector/bgp/update/af.py
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: ISC
+
+# Copyright 2023 6WIND S.A.
+# Authored by Farid Mihoub <farid.mihoub@6wind.com>
+#
+
+# IANA Address Family Identifier
+AFI_IP = 1
+AFI_IP6 = 2
+AFI_L2VPN = 25
+
+# IANA Subsequent Address Family Idenitifier
+SAFI_UNICAST = 1
+SAFI_MULTICAST = 2
+SAFI_MPLS_LABEL = 4
+SAFI_EVPN = 70
+SAFI_MPLS_VPN = 128
+SAFI_IP_FLOWSPEC = 133
+SAFI_VPN_FLOWSPEC = 134
+
+
+#------------------------------------------------------------------------------
+class AddressFamily:
+ def __init__(self, afi, safi):
+ self.afi = afi
+ self.safi = safi
+
+ def __eq__(self, other):
+ if not isinstance(other, type(self)):
+ return False
+ return (self.afi, self.safi) == (other.afi, other.safi)
+
+ def __str__(self):
+ return f'afi: {self.afi}, safi: {self.safi}'
+
+ def __hash__(self):
+ return hash((self.afi, self.safi))
+
+
+#------------------------------------------------------------------------------
+class AF:
+ IPv4_UNICAST = AddressFamily(AFI_IP, SAFI_UNICAST)
+ IPv6_UNICAST = AddressFamily(AFI_IP6, SAFI_UNICAST)
+ IPv4_VPN = AddressFamily(AFI_IP, SAFI_MPLS_VPN)
+ IPv6_VPN = AddressFamily(AFI_IP6, SAFI_MPLS_VPN)
+ IPv4_MPLS = AddressFamily(AFI_IP, SAFI_MPLS_LABEL)
+ IPv6_MPLS = AddressFamily(AFI_IP6, SAFI_MPLS_LABEL)
+ IPv4_FLOWSPEC = AddressFamily(AFI_IP, SAFI_IP_FLOWSPEC)
+ IPv6_FLOWSPEC = AddressFamily(AFI_IP6, SAFI_IP_FLOWSPEC)
+ VPNv4_FLOWSPEC = AddressFamily(AFI_IP, SAFI_VPN_FLOWSPEC)
+ VPNv6_FLOWSPEC = AddressFamily(AFI_IP6, SAFI_VPN_FLOWSPEC)
+ L2EVPN = AddressFamily(AFI_L2VPN, SAFI_EVPN)
+ L2VPN_FLOWSPEC = AddressFamily(AFI_L2VPN, SAFI_VPN_FLOWSPEC)
diff --git a/tests/topotests/lib/bmp_collector/bgp/update/nlri.py b/tests/topotests/lib/bmp_collector/bgp/update/nlri.py
new file mode 100644
index 0000000..c1720f1
--- /dev/null
+++ b/tests/topotests/lib/bmp_collector/bgp/update/nlri.py
@@ -0,0 +1,140 @@
+# SPDX-License-Identifier: ISC
+
+# Copyright 2023 6WIND S.A.
+# Authored by Farid Mihoub <farid.mihoub@6wind.com>
+#
+import ipaddress
+import struct
+
+from .af import AddressFamily, AF
+from .rd import RouteDistinguisher
+
+
+def decode_label(label):
+ # from frr
+ # frr encode just one label
+ return (label[0] << 12) | (label[1] << 4) | (label[2] & 0xf0) >> 4
+
+def padding(databin, len_):
+ """
+ Assumption:
+ One nlri per update/withdraw message, so we can add
+ a padding to the prefix without worrying about its length
+ """
+ if len(databin) >= len_:
+ return databin
+ return databin + b'\0' * (len_ - len(databin))
+
+def dissect_nlri(nlri_data, afi, safi):
+ """
+ Exract nlri information based on the address family
+ """
+ addr_family = AddressFamily(afi, safi)
+ if addr_family == AF.IPv6_VPN:
+ return NlriIPv6Vpn.parse(nlri_data)
+ elif addr_family == AF.IPv4_VPN:
+ return NlriIPv4Vpn.parse(nlri_data)
+ elif addr_family == AF.IPv6_UNICAST:
+ return NlriIPv6Unicast.parse(nlri_data)
+
+ return {'ip_prefix': 'Unknown'}
+
+
+#------------------------------------------------------------------------------
+class NlriIPv4Unicast:
+
+ @staticmethod
+ def parse(data):
+ """parses prefixes from withdrawn_routes or nrli data"""
+ (prefix_len,) = struct.unpack_from('!B', data)
+ prefix = padding(data[1:], 4)
+
+ return {'ip_prefix': f'{ipaddress.IPv4Address(prefix)}/{prefix_len}'}
+
+
+#------------------------------------------------------------------------------
+class NlriIPv6Unicast:
+ @staticmethod
+ def parse(data):
+ """parses prefixes from withdrawn_routes or nrli data"""
+ (prefix_len,) = struct.unpack_from('!B', data)
+ prefix = padding(data[1:], 16)
+
+ return {'ip_prefix': f'{ipaddress.IPv6Address(prefix)}/{prefix_len}'}
+
+
+#------------------------------------------------------------------------------
+class NlriIPv4Vpn:
+ UNPACK_STR = '!B3s8s'
+
+ @classmethod
+ def parse(cls, data):
+ (bit_len, label, rd) = struct.unpack_from(cls.UNPACK_STR, data)
+ offset = struct.calcsize(cls.UNPACK_STR)
+
+ ipv4 = padding(data[offset:], 4)
+ # prefix_len = total_bits_len - label_bits_len - rd_bits_len
+ prefix_len = bit_len - 3*8 - 8*8
+ return {
+ 'label': decode_label(label),
+ 'rd': str(RouteDistinguisher(rd)),
+ 'ip_prefix': f'{ipaddress.IPv4Address(ipv4)}/{prefix_len}',
+ }
+
+
+#------------------------------------------------------------------------------
+class NlriIPv6Vpn:
+ UNPACK_STR = '!B3s8s'
+
+ @classmethod
+ def parse(cls, data):
+ # rfc 3107, 8227
+ (bit_len, label, rd) = struct.unpack_from(cls.UNPACK_STR, data)
+ offset = struct.calcsize(cls.UNPACK_STR)
+
+ ipv6 = padding(data[offset:], 16)
+ prefix_len = bit_len - 3*8 - 8*8
+ return {
+ 'label': decode_label(label),
+ 'rd': str(RouteDistinguisher(rd)),
+ 'ip_prefix': f'{ipaddress.IPv6Address(ipv6)}/{prefix_len}',
+ }
+
+
+#------------------------------------------------------------------------------
+class NlriIPv4Mpls:
+ pass
+
+
+#------------------------------------------------------------------------------
+class NlriIPv6Mpls:
+ pass
+
+
+#------------------------------------------------------------------------------
+class NlriIPv4FlowSpec:
+ pass
+
+
+#------------------------------------------------------------------------------
+class NlriIPv6FlowSpec:
+ pass
+
+
+#------------------------------------------------------------------------------
+class NlriVpn4FlowSpec:
+ pass
+
+
+#------------------------------------------------------------------------------
+class NlriVpn6FlowSpec:
+ pass
+
+
+#------------------------------------------------------------------------------
+class NlriL2EVPN:
+ pass
+
+#------------------------------------------------------------------------------
+class NlriL2VPNFlowSpec:
+ pass
diff --git a/tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py b/tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py
new file mode 100644
index 0000000..6e82e9c
--- /dev/null
+++ b/tests/topotests/lib/bmp_collector/bgp/update/path_attributes.py
@@ -0,0 +1,304 @@
+# SPDX-License-Identifier: ISC
+
+# Copyright 2023 6WIND S.A.
+# Authored by Farid Mihoub <farid.mihoub@6wind.com>
+#
+import struct
+import ipaddress
+
+from . import nlri as NLRI
+from .af import AddressFamily, AF
+from .rd import RouteDistinguisher
+
+
+PATH_ATTR_FLAG_OPTIONAL = 1 << 7
+PATH_ATTR_FLAG_TRANSITIVE = 1 << 6
+PATH_ATTR_FLAG_PARTIAL = 1 << 5
+PATH_ATTR_FLAG_EXTENDED_LENGTH = 1 << 4
+
+PATH_ATTR_TYPE_ORIGIN = 1
+PATH_ATTR_TYPE_AS_PATH = 2
+PATH_ATTR_TYPE_NEXT_HOP = 3
+PATH_ATTR_TYPE_MULTI_EXIT_DISC = 4
+PATH_ATTR_TYPE_LOCAL_PREF = 5
+PATH_ATTR_TYPE_ATOMIC_AGGREGATE = 6
+PATH_ATTR_TYPE_AGGREGATOR = 7
+PATH_ATTR_TYPE_COMMUNITIES = 8
+PATH_ATTR_TYPE_ORIGINATOR_ID = 9
+PATH_ATTR_TYPE_CLUSTER_LIST = 10
+PATH_ATTR_TYPE_MP_REACH_NLRI = 14
+PATH_ATTR_TYPE_MP_UNREACH_NLRI = 15
+PATH_ATTR_TYPE_EXTENDED_COMMUNITIES = 16
+PATH_ATTR_TYPE_AS4_PATH = 17
+PATH_ATTR_TYPE_AS4_AGGREGATOR = 18
+PATH_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE = 22
+
+ORIGIN_IGP = 0x00
+ORIGIN_EGP = 0x01
+ORIGIN_INCOMPLETE = 0x02
+
+
+#------------------------------------------------------------------------------
+class PathAttribute:
+ PATH_ATTRS = {}
+ UNKNOWN_ATTR = None
+ UNPACK_STR = '!BB'
+
+ @classmethod
+ def register_path_attr(cls, path_attr):
+ def _register_path_attr(subcls):
+ cls.PATH_ATTRS[path_attr] = subcls
+ return subcls
+ return _register_path_attr
+
+ @classmethod
+ def lookup_path_attr(cls, type_code):
+ return cls.PATH_ATTRS.get(type_code, cls.UNKNOWN_ATTR)
+
+ @classmethod
+ def dissect(cls, data):
+ flags, type_code = struct.unpack_from(cls.UNPACK_STR, data)
+ offset = struct.calcsize(cls.UNPACK_STR)
+
+ # get attribute length
+ attr_len_str = '!H' if (flags & PATH_ATTR_FLAG_EXTENDED_LENGTH) else '!B'
+
+ (attr_len,) = struct.unpack_from(attr_len_str, data[offset:])
+
+ offset += struct.calcsize(attr_len_str)
+
+ path_attr_cls = cls.lookup_path_attr(type_code)
+ if path_attr_cls == cls.UNKNOWN_ATTR:
+ return data[offset + attr_len:], None
+
+ return data[offset+attr_len:], path_attr_cls.dissect(data[offset:offset+attr_len])
+
+
+#------------------------------------------------------------------------------
+@PathAttribute.register_path_attr(PATH_ATTR_TYPE_ORIGIN)
+class PathAttrOrigin:
+ ORIGIN_STR = {
+ ORIGIN_IGP: 'IGP',
+ ORIGIN_EGP: 'EGP',
+ ORIGIN_INCOMPLETE: 'INCOMPLETE',
+ }
+
+ @classmethod
+ def dissect(cls, data):
+ (origin,) = struct.unpack_from('!B', data)
+
+ return {'origin': cls.ORIGIN_STR.get(origin, 'UNKNOWN')}
+
+
+#------------------------------------------------------------------------------
+@PathAttribute.register_path_attr(PATH_ATTR_TYPE_AS_PATH)
+class PathAttrAsPath:
+ AS_PATH_TYPE_SET = 0x01
+ AS_PATH_TYPE_SEQUENCE= 0x02
+
+ @staticmethod
+ def get_asn_len(asns):
+ """XXX: Add this nightmare to determine the ASN length"""
+ pass
+
+ @classmethod
+ def dissect(cls, data):
+ (_type, _len) = struct.unpack_from('!BB', data)
+ data = data[2:]
+
+ _type_str = 'Ordred' if _type == cls.AS_PATH_TYPE_SEQUENCE else 'Raw'
+ segment = []
+ while data:
+ (asn,) = struct.unpack_from('!I', data)
+ segment.append(asn)
+ data = data[4:]
+
+ return {'as_path': ' '.join(str(a) for a in segment)}
+
+
+#------------------------------------------------------------------------------
+@PathAttribute.register_path_attr(PATH_ATTR_TYPE_NEXT_HOP)
+class PathAttrNextHop:
+ @classmethod
+ def dissect(cls, data):
+ (nexthop,) = struct.unpack_from('!4s', data)
+ return {'bgp_nexthop': str(ipaddress.IPv4Address(nexthop))}
+
+
+#------------------------------------------------------------------------------
+class PathAttrMultiExitDisc:
+ pass
+
+
+#------------------------------------------------------------------------------
+@PathAttribute.register_path_attr(PATH_ATTR_TYPE_MP_REACH_NLRI)
+class PathAttrMpReachNLRI:
+ """
+ +---------------------------------------------------------+
+ | Address Family Identifier (2 octets) |
+ +---------------------------------------------------------+
+ | Subsequent Address Family Identifier (1 octet) |
+ +---------------------------------------------------------+
+ | Length of Next Hop Network Address (1 octet) |
+ +---------------------------------------------------------+
+ | Network Address of Next Hop (variable) |
+ +---------------------------------------------------------+
+ | Number of SNPAs (1 octet) |
+ +---------------------------------------------------------+
+ | Length of first SNPA(1 octet) |
+ +---------------------------------------------------------+
+ | First SNPA (variable) |
+ +---------------------------------------------------------+
+ | Length of second SNPA (1 octet) |
+ +---------------------------------------------------------+
+ | Second SNPA (variable) |
+ +---------------------------------------------------------+
+ | ... |
+ +---------------------------------------------------------+
+ | Length of Last SNPA (1 octet) |
+ +---------------------------------------------------------+
+ | Last SNPA (variable) |
+ +---------------------------------------------------------+
+ | Network Layer Reachability Information (variable) |
+ +---------------------------------------------------------+
+ """
+ UNPACK_STR = '!HBB'
+ NLRI_RESERVED_LEN = 1
+
+ @staticmethod
+ def dissect_nexthop(nexthop_data, nexthop_len):
+ msg = {}
+ if nexthop_len == 4:
+ # IPv4
+ (ipv4,) = struct.unpack_from('!4s', nexthop_data)
+ msg['nxhp_ip'] = str(ipaddress.IPv4Address(ipv4))
+ elif nexthop_len == 12:
+ # RD + IPv4
+ (rd, ipv4) = struct.unpack_from('!8s4s', nexthop_data)
+ msg['nxhp_ip'] = str(ipaddress.IPv4Address(ipv4))
+ msg['nxhp_rd'] = str(RouteDistinguisher(rd))
+ elif nexthop_len == 16:
+ # IPv6
+ (ipv6,) = struct.unpack_from('!16s', nexthop_data)
+ msg['nxhp_ip'] = str(ipaddress.IPv6Address(ipv6))
+ elif nexthop_len == 24:
+ # RD + IPv6
+ (rd, ipv6) = struct.unpack_from('!8s16s', nexthop_data)
+ msg['nxhp_ip'] = str(ipaddress.IPv6Address(ipv6))
+ msg['nxhp_rd'] = str(RouteDistinguisher(rd))
+ elif nexthop_len == 32:
+ # IPv6 + IPv6 link-local
+ (ipv6, link_local)= struct.unpack_from('!16s16s', nexthop_data)
+ msg['nxhp_ip'] = str(ipaddress.IPv6Address(ipv6))
+ msg['nxhp_link-local'] = str(ipaddress.IPv6Address(link_local))
+ elif nexthop_len == 48:
+ # RD + IPv6 + RD + IPv6 link-local
+ u_str = '!8s16s8s16s'
+ (rd1, ipv6, rd2, link_local)= struct.unpack_from(u_str, nexthop_data)
+ msg['nxhp_rd1'] = str(RouteDistinguisher(rd1))
+ msg['nxhp_ip'] = str(ipaddress.IPv6Address(ipv6))
+ msg['nxhp_rd2'] = str(RouteDistinguisher(rd2))
+ msg['nxhp_link-local'] = str(ipaddress.IPv6Address(link_local))
+
+ return msg
+
+ @staticmethod
+ def dissect_snpa(snpa_data):
+ pass
+
+ @classmethod
+ def dissect(cls, data):
+ (afi, safi, nexthop_len) = struct.unpack_from(cls.UNPACK_STR, data)
+ offset = struct.calcsize(cls.UNPACK_STR)
+ msg = {'afi': afi, 'safi': safi}
+
+ # dissect nexthop
+ nexthop_data = data[offset: offset + nexthop_len]
+ nexthop = cls.dissect_nexthop(nexthop_data, nexthop_len)
+ msg.update(nexthop)
+
+ offset += nexthop_len
+ # dissect snpa or just reserved
+ offset += 1
+ # dissect nlri
+ nlri = NLRI.dissect_nlri(data[offset:], afi, safi)
+ msg.update(nlri)
+
+ return msg
+
+
+#------------------------------------------------------------------------------
+@PathAttribute.register_path_attr(PATH_ATTR_TYPE_MP_UNREACH_NLRI)
+class PathAttrMpUnReachNLRI:
+ """
+ +---------------------------------------------------------+
+ | Address Family Identifier (2 bytes) |
+ +---------------------------------------------------------+
+ | Subsequent Address Family Identifier (1 byte) |
+ +---------------------------------------------------------+
+ | Withdrawn Routes (variable) |
+ +---------------------------------------------------------+
+ """
+ UNPACK_STR = '!HB'
+
+ @classmethod
+ def dissect(cls, data):
+ (afi, safi) = struct.unpack_from(cls.UNPACK_STR, data)
+ offset = struct.calcsize(cls.UNPACK_STR)
+ msg = {'bmp_log_type': 'withdraw','afi': afi, 'safi': safi}
+
+ if data[offset:]:
+ # dissect withdrawn_routes
+ msg.update(NLRI.dissect_nlri(data[offset:], afi, safi))
+
+ return msg
+
+
+#------------------------------------------------------------------------------
+class PathAttrLocalPref:
+ pass
+
+
+#------------------------------------------------------------------------------
+class PathAttrAtomicAgregate:
+ pass
+
+
+#------------------------------------------------------------------------------
+class PathAttrAggregator:
+ pass
+
+
+#------------------------------------------------------------------------------
+class PathAttrCommunities:
+ pass
+
+
+#------------------------------------------------------------------------------
+class PathAttrOriginatorID:
+ pass
+
+
+#------------------------------------------------------------------------------
+class PathAttrClusterList:
+ pass
+
+
+#------------------------------------------------------------------------------
+class PathAttrExtendedCommunities:
+ pass
+
+
+#------------------------------------------------------------------------------
+class PathAttrPMSITunnel:
+ pass
+
+
+#------------------------------------------------------------------------------
+class PathAttrLinkState:
+ pass
+
+
+#------------------------------------------------------------------------------
+class PathAttrLargeCommunities:
+ pass
diff --git a/tests/topotests/lib/bmp_collector/bgp/update/rd.py b/tests/topotests/lib/bmp_collector/bgp/update/rd.py
new file mode 100644
index 0000000..c382fa8
--- /dev/null
+++ b/tests/topotests/lib/bmp_collector/bgp/update/rd.py
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: ISC
+
+# Copyright 2023 6WIND S.A.
+# Authored by Farid Mihoub <farid.mihoub@6wind.com>
+#
+import ipaddress
+import struct
+
+
+#------------------------------------------------------------------------------
+class RouteDistinguisher:
+ """
+ type 0:
+ +---------------------------------------------------------------------+
+ + type=0 (2 bytes)| Administrator subfield | Assigned number subfiled |
+ + | AS number (2 bytes) | Service Provider 4 bytes)|
+ +---------------------------------------------------------------------+
+
+ type 1:
+ +---------------------------------------------------------------------+
+ + type=1 (2 bytes)| Administrator subfield | Assigned number subfiled |
+ + | IPv4 (4 bytes) | Service Provider 2 bytes)|
+ +---------------------------------------------------------------------+
+
+ type 2:
+ +-------------------------------------------------------------------------+
+ + type=2 (2 bytes)| Administrator subfield | Assigned number subfiled |
+ + | 4-bytes AS number (4 bytes)| Service Provider 2 bytes)|
+ +-------------------------------------------------------------------------+
+ """
+ def __init__(self, rd):
+ self.rd = rd
+ self.as_number = None
+ self.admin_ipv4 = None
+ self.four_bytes_as = None
+ self.assigned_sp = None
+ self.repr_str = ''
+ self.dissect()
+
+ def dissect(self):
+ (rd_type,) = struct.unpack_from('!H', self.rd)
+ if rd_type == 0:
+ (self.as_number,
+ self.assigned_sp) = struct.unpack_from('!HI', self.rd[2:])
+ self.repr_str = f'{self.as_number}:{self.assigned_sp}'
+
+ elif rd_type == 1:
+ (self.admin_ipv4,
+ self.assigned_sp) = struct.unpack_from('!IH', self.rd[2:])
+ ipv4 = str(ipaddress.IPv4Address(self.admin_ipv4))
+ self.repr_str = f'{self.as_number}:{self.assigned_sp}'
+
+ elif rd_type == 2:
+ (self.four_bytes_as,
+ self.assigned_sp) = struct.unpack_from('!IH', self.rd[2:])
+ self.repr_str = f'{self.four_bytes_as}:{self.assigned_sp}'
+
+ def __str__(self):
+ return self.repr_str
diff --git a/tests/topotests/lib/bmp_collector/bmp.py b/tests/topotests/lib/bmp_collector/bmp.py
new file mode 100644
index 0000000..b07329c
--- /dev/null
+++ b/tests/topotests/lib/bmp_collector/bmp.py
@@ -0,0 +1,420 @@
+# SPDX-License-Identifier: ISC
+
+# Copyright 2023 6WIND S.A.
+# Authored by Farid Mihoub <farid.mihoub@6wind.com>
+#
+"""
+BMP main module:
+ - dissect monitoring messages in the way to get updated/withdrawed prefixes
+ - XXX: missing RFCs references
+ - XXX: more bmp messages types to dissect
+ - XXX: complete bgp message dissection
+"""
+import datetime
+import ipaddress
+import json
+import os
+import struct
+
+from bgp.update import BGPUpdate
+from bgp.update.rd import RouteDistinguisher
+
+
+SEQ = 0
+LOG_DIR = "/var/log/"
+LOG_FILE = "/var/log/bmp.log"
+
+IS_ADJ_RIB_OUT = 1 << 4
+IS_AS_PATH = 1 << 5
+IS_POST_POLICY = 1 << 6
+IS_IPV6 = 1 << 7
+IS_FILTERED = 1 << 7
+
+if not os.path.exists(LOG_DIR):
+ os.makedirs(LOG_DIR)
+
+def bin2str_ipaddress(ip_bytes, is_ipv6=False):
+ if is_ipv6:
+ return str(ipaddress.IPv6Address(ip_bytes))
+ return str(ipaddress.IPv4Address(ip_bytes[-4:]))
+
+def log2file(logs):
+ """
+ XXX: extract the useful information and save it in a flat dictionnary
+ """
+ with open(LOG_FILE, 'a') as f:
+ f.write(json.dumps(logs) + "\n")
+
+
+#------------------------------------------------------------------------------
+class BMPCodes:
+ """
+ XXX: complete the list, provide RFCs.
+ """
+ VERSION = 0x3
+
+ BMP_MSG_TYPE_ROUTE_MONITORING = 0x00
+ BMP_MSG_TYPE_STATISTICS_REPORT = 0x01
+ BMP_MSG_TYPE_PEER_DOWN_NOTIFICATION = 0x02
+ BMP_MSG_TYPE_PEER_UP_NOTIFICATION = 0x03
+ BMP_MSG_TYPE_INITIATION = 0x04
+ BMP_MSG_TYPE_TERMINATION = 0x05
+ BMP_MSG_TYPE_ROUTE_MIRRORING = 0x06
+ BMP_MSG_TYPE_ROUTE_POLICY = 0x64
+
+ # initiation message types
+ BMP_INIT_INFO_STRING = 0x00
+ BMP_INIT_SYSTEM_DESCRIPTION = 0x01
+ BMP_INIT_SYSTEM_NAME = 0x02
+ BMP_INIT_VRF_TABLE_NAME = 0x03
+ BMP_INIT_ADMIN_LABEL = 0x04
+
+ # peer types
+ BMP_PEER_GLOBAL_INSTANCE = 0x00
+ BMP_PEER_RD_INSTANCE = 0x01
+ BMP_PEER_LOCAL_INSTANCE = 0x02
+ BMP_PEER_LOC_RIB_INSTANCE = 0x03
+
+ # peer header flags
+ BMP_PEER_FLAG_IPV6 = 0x80
+ BMP_PEER_FLAG_POST_POLICY = 0x40
+ BMP_PEER_FLAG_AS_PATH = 0x20
+ BMP_PEER_FLAG_ADJ_RIB_OUT = 0x10
+
+ # peer loc-rib flag
+ BMP_PEER_FLAG_LOC_RIB = 0x80
+ BMP_PEER_FLAG_LOC_RIB_RES = 0x7F
+
+ # statistics type
+ BMP_STAT_PREFIX_REJ = 0x00
+ BMP_STAT_PREFIX_DUP = 0x01
+ BMP_STAT_WITHDRAW_DUP = 0x02
+ BMP_STAT_CLUSTER_LOOP = 0x03
+ BMP_STAT_AS_LOOP = 0x04
+ BMP_STAT_INV_ORIGINATOR = 0x05
+ BMP_STAT_AS_CONFED_LOOP = 0x06
+ BMP_STAT_ROUTES_ADJ_RIB_IN = 0x07
+ BMP_STAT_ROUTES_LOC_RIB = 0x08
+ BMP_STAT_ROUTES_PER_ADJ_RIB_IN = 0x09
+ BMP_STAT_ROUTES_PER_LOC_RIB = 0x0A
+ BMP_STAT_UPDATE_TREAT = 0x0B
+ BMP_STAT_PREFIXES_TREAT = 0x0C
+ BMP_STAT_DUPLICATE_UPDATE = 0x0D
+ BMP_STAT_ROUTES_PRE_ADJ_RIB_OUT = 0x0E
+ BMP_STAT_ROUTES_POST_ADJ_RIB_OUT = 0x0F
+ BMP_STAT_ROUTES_PRE_PER_ADJ_RIB_OUT = 0x10
+ BMP_STAT_ROUTES_POST_PER_ADJ_RIB_OUT = 0x11
+
+ # peer down reason code
+ BMP_PEER_DOWN_LOCAL_NOTIFY = 0x01
+ BMP_PEER_DOWN_LOCAL_NO_NOTIFY = 0X02
+ BMP_PEER_DOWN_REMOTE_NOTIFY = 0X03
+ BMP_PEER_DOWN_REMOTE_NO_NOTIFY = 0X04
+ BMP_PEER_DOWN_INFO_NO_LONGER = 0x05
+ BMP_PEER_DOWN_SYSTEM_CLOSED = 0X06
+
+ # termincation message types
+ BMP_TERM_TYPE_STRING = 0x00
+ BMP_TERM_TYPE_REASON = 0X01
+
+ # termination reason code
+ BMP_TERM_REASON_ADMIN_CLOSE = 0x00
+ BMP_TERM_REASON_UNSPECIFIED = 0x01
+ BMP_TERM_REASON_RESOURCES = 0x02
+ BMP_TERM_REASON_REDUNDANT = 0x03
+ BMP_TERM_REASON_PERM_CLOSE = 0x04
+
+ # policy route tlv
+ BMP_ROUTE_POLICY_TLV_VRF = 0x00
+ BMP_ROUTE_POLICY_TLV_POLICY= 0x01
+ BMP_ROUTE_POLICY_TLV_PRE_POLICY = 0x02
+ BMP_ROUTE_POLICY_TLV_POST_POLICY = 0x03
+ BMP_ROUTE_POLICY_TLV_STRING = 0x04
+
+
+#------------------------------------------------------------------------------
+class BMPMsg:
+ """
+ XXX: should we move register_msg_type and look_msg_type
+ to generic Type class.
+ """
+ TYPES = {}
+ UNKNOWN_TYPE = None
+ HDR_STR = '!BIB'
+ MIN_LEN = struct.calcsize(HDR_STR)
+ TYPES_STR = {
+ BMPCodes.BMP_MSG_TYPE_INITIATION: 'initiation',
+ BMPCodes.BMP_MSG_TYPE_PEER_DOWN_NOTIFICATION: 'peer down notification',
+ BMPCodes.BMP_MSG_TYPE_PEER_UP_NOTIFICATION: 'peer up notification',
+ BMPCodes.BMP_MSG_TYPE_ROUTE_MONITORING: 'route monitoring',
+ BMPCodes.BMP_MSG_TYPE_STATISTICS_REPORT: 'statistics report',
+ BMPCodes.BMP_MSG_TYPE_TERMINATION: 'termination',
+ BMPCodes.BMP_MSG_TYPE_ROUTE_MIRRORING: 'route mirroring',
+ BMPCodes.BMP_MSG_TYPE_ROUTE_POLICY: 'route policy',
+ }
+
+ @classmethod
+ def register_msg_type(cls, msgtype):
+ def _register_type(subcls):
+ cls.TYPES[msgtype] = subcls
+ return subcls
+ return _register_type
+
+ @classmethod
+ def lookup_msg_type(cls, msgtype):
+ return cls.TYPES.get(msgtype, cls.UNKNOWN_TYPE)
+
+ @classmethod
+ def dissect_header(cls, data):
+ """
+ 0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Version |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Message Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Message Type |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ """
+ if len(data) < cls.MIN_LEN:
+ pass
+ else:
+ _version, _len, _type = struct.unpack(cls.HDR_STR, data[0:cls.MIN_LEN])
+ return _version, _len, _type
+
+ @classmethod
+ def dissect(cls, data):
+ global SEQ
+ version, msglen, msgtype = cls.dissect_header(data)
+
+ msg_data = data[cls.MIN_LEN:msglen]
+ data = data[msglen:]
+
+ if version != BMPCodes.VERSION:
+ # XXX: log something
+ return data
+
+ msg_cls = cls.lookup_msg_type(msgtype)
+ if msg_cls == cls.UNKNOWN_TYPE:
+ # XXX: log something
+ return data
+
+ msg_cls.MSG_LEN = msglen - cls.MIN_LEN
+ logs = msg_cls.dissect(msg_data)
+ logs["seq"] = SEQ
+ log2file(logs)
+ SEQ += 1
+
+ return data
+
+
+#------------------------------------------------------------------------------
+class BMPPerPeerMessage:
+ """
+ 0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Peer Type | Peer Flags |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Peer Address (16 bytes) |
+ ~ ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Peer AS |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Peer BGP ID |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Timestamp (seconds) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Timestamp (microseconds) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ """
+ PEER_UNPACK_STR = '!BB8s16sI4sII'
+ PEER_TYPE_STR = {
+ BMPCodes.BMP_PEER_GLOBAL_INSTANCE: 'global instance',
+ BMPCodes.BMP_PEER_RD_INSTANCE: 'route distinguisher instance',
+ BMPCodes.BMP_PEER_LOCAL_INSTANCE: 'local instance',
+ BMPCodes.BMP_PEER_LOC_RIB_INSTANCE: 'loc-rib instance',
+ }
+
+ @classmethod
+ def dissect(cls, data):
+ (peer_type,
+ peer_flags,
+ peer_distinguisher,
+ peer_address,
+ peer_asn,
+ peer_bgp_id,
+ timestamp_secs,
+ timestamp_microsecs) = struct.unpack_from(cls.PEER_UNPACK_STR, data)
+
+ msg = {'peer_type': cls.PEER_TYPE_STR[peer_type]}
+
+ if peer_type == 0x03:
+ msg['is_filtered'] = bool(peer_flags & IS_FILTERED)
+ else:
+ # peer_flags = 0x0000 0000
+ # ipv6, post-policy, as-path, adj-rib-out, reserverdx4
+ is_adj_rib_out = bool(peer_flags & IS_ADJ_RIB_OUT)
+ is_as_path = bool(peer_flags & IS_AS_PATH)
+ is_post_policy = bool(peer_flags & IS_POST_POLICY)
+ is_ipv6 = bool(peer_flags & IS_IPV6)
+ msg['post_policy'] = is_post_policy
+ msg['ipv6'] = is_ipv6
+ msg['peer_ip'] = bin2str_ipaddress(peer_address, is_ipv6)
+
+
+ peer_bgp_id = bin2str_ipaddress(peer_bgp_id)
+ timestamp = float(timestamp_secs) + timestamp_microsecs * (10 ** -6)
+
+ data = data[struct.calcsize(cls.PEER_UNPACK_STR):]
+ msg.update({
+ 'peer_distinguisher': str(RouteDistinguisher(peer_distinguisher)),
+ 'peer_asn': peer_asn,
+ 'peer_bgp_id': peer_bgp_id,
+ 'timestamp': str(datetime.datetime.fromtimestamp(timestamp)),
+ })
+
+ return data, msg
+
+
+#------------------------------------------------------------------------------
+@BMPMsg.register_msg_type(BMPCodes.BMP_MSG_TYPE_ROUTE_MONITORING)
+class BMPRouteMonitoring(BMPPerPeerMessage):
+
+ @classmethod
+ def dissect(cls, data):
+ data, peer_msg = super().dissect(data)
+ data, update_msg = BGPUpdate.dissect(data)
+ return {**peer_msg, **update_msg}
+
+
+#------------------------------------------------------------------------------
+class BMPStatisticsReport:
+ """
+ 0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Stats Count |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Stat Type | Stat Len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Stat Data |
+ ~ ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ """
+ pass
+
+
+#------------------------------------------------------------------------------
+class BMPPeerDownNotification:
+ """
+ 0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Reason |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Data (present if Reason = 1, 2 or 3) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ """
+ pass
+
+
+#------------------------------------------------------------------------------
+@BMPMsg.register_msg_type(BMPCodes.BMP_MSG_TYPE_PEER_UP_NOTIFICATION)
+class BMPPeerUpNotification(BMPPerPeerMessage):
+ """
+ 0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Local Address (16 bytes) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Local Port | Remote Port |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Sent OPEN Message #|
+ ~ ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Received OPEN Message |
+ ~ ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ """
+ UNPACK_STR = '!16sHH'
+ MIN_LEN = struct.calcsize(UNPACK_STR)
+ MSG_LEN = None
+
+ @classmethod
+ def dissect(cls, data):
+ data, peer_msg = super().dissect(data)
+
+ (local_addr,
+ local_port,
+ remote_port) = struct.unpack_from(cls.UNPACK_STR, data)
+
+ msg = {
+ **peer_msg,
+ **{
+ 'local_ip': bin2str_ipaddress(local_addr, peer_msg.get('ipv6')),
+ 'local_port': int(local_port),
+ 'remote_port': int(remote_port),
+ },
+ }
+
+ # XXX: dissect the bgp open message
+
+ return msg
+
+
+#------------------------------------------------------------------------------
+@BMPMsg.register_msg_type(BMPCodes.BMP_MSG_TYPE_INITIATION)
+class BMPInitiation:
+ """
+ 0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Information Type | Information Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Information (variable) |
+ ~ ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ """
+ TLV_STR = '!HH'
+ MIN_LEN = struct.calcsize(TLV_STR)
+ FIELD_TO_STR = {
+ BMPCodes.BMP_INIT_INFO_STRING: 'information',
+ BMPCodes.BMP_INIT_ADMIN_LABEL: 'admin_label',
+ BMPCodes.BMP_INIT_SYSTEM_DESCRIPTION: 'system_description',
+ BMPCodes.BMP_INIT_SYSTEM_NAME: 'system_name',
+ BMPCodes.BMP_INIT_VRF_TABLE_NAME: 'vrf_table_name',
+ }
+
+ @classmethod
+ def dissect(cls, data):
+ msg = {}
+ while len(data) > cls.MIN_LEN:
+ _type, _len = struct.unpack_from(cls.TLV_STR, data[0:cls.MIN_LEN])
+ _value = data[cls.MIN_LEN: cls.MIN_LEN + _len].decode()
+
+ msg[cls.FIELD_TO_STR[_type]] = _value
+ data = data[cls.MIN_LEN + _len:]
+
+ return msg
+
+
+#------------------------------------------------------------------------------
+class BMPTermination:
+ """
+ 0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Information Type | Information Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Information (variable) |
+ ~ ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ """
+ pass
+
+
+#------------------------------------------------------------------------------
+class BMPRouteMirroring:
+ pass
+
+
+#------------------------------------------------------------------------------
+class BMPRoutePolicy:
+ pass
diff --git a/tests/topotests/lib/bmp_collector/bmpserver b/tests/topotests/lib/bmp_collector/bmpserver
new file mode 100755
index 0000000..25b4a52
--- /dev/null
+++ b/tests/topotests/lib/bmp_collector/bmpserver
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: ISC
+
+# Copyright 2023 6WIND S.A.
+# Authored by Farid Mihoub <farid.mihoub@6wind.com>
+#
+import argparse
+# XXX: something more reliable should be used "Twisted" a great choice.
+import socket
+import sys
+
+from bmp import BMPMsg
+
+BGP_MAX_SIZE = 4096
+
+parser = argparse.ArgumentParser()
+parser.add_argument("-a", "--address", type=str, default="0.0.0.0")
+parser.add_argument("-p", "--port", type=int, default=1789)
+
+def main():
+ args = parser.parse_args()
+ ADDRESS, PORT = args.address, args.port
+
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind((ADDRESS, PORT))
+ s.listen()
+ connection, _ = s.accept()
+
+ try:
+ while True:
+ data = connection.recv(BGP_MAX_SIZE)
+ while len(data) > BMPMsg.MIN_LEN:
+ data = BMPMsg.dissect(data)
+ except Exception as e:
+ # XXX: do something
+ pass
+ except KeyboardInterrupt:
+ # XXX: do something
+ pass
+ finally:
+ connection.close()
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/tests/topotests/lib/checkping.py b/tests/topotests/lib/checkping.py
new file mode 100644
index 0000000..aaa6164
--- /dev/null
+++ b/tests/topotests/lib/checkping.py
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright 2023 Quentin Young
+
+import functools
+from lib.topogen import get_topogen
+from lib.topolog import logger
+from lib import topotest
+
+
+def check_ping(name, dest_addr, expect_connected, count, wait):
+ """
+ Assert that ping to dest_addr is expected
+ * 'name': the router to set the ping from
+ * 'dest_addr': The destination ip address to ping
+ * 'expect_connected': True if ping is expected to pass
+ * 'count': how many echos to send
+ * 'wait': how long ping should wait to receive all replies
+ """
+
+ 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 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=count, wait=wait)
+ assert result is None, "Failed"
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
new file mode 100644
index 0000000..e19d96f
--- /dev/null
+++ b/tests/topotests/lib/common_config.py
@@ -0,0 +1,4960 @@
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+import functools
+import ipaddress
+import json
+import os
+import platform
+import socket
+import subprocess
+import sys
+import traceback
+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 munet.testing.util import pause_test
+
+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",
+ ],
+ "pim6d": [
+ "debug pimv6 events",
+ "debug pimv6 packets",
+ "debug pimv6 packet-dump send",
+ "debug pimv6 packet-dump receive",
+ "debug pimv6 trace",
+ "debug pimv6 trace detail",
+ "debug pimv6 zebra",
+ "debug pimv6 bsm",
+ "debug pimv6 packets hello",
+ "debug pimv6 packets joins",
+ "debug pimv6 packets register",
+ "debug pimv6 nht",
+ "debug pimv6 nht detail",
+ "debug mroute6",
+ "debug mroute6 detail",
+ "debug mld events",
+ "debug mld packets",
+ "debug mld trace",
+ ],
+ "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",
+ ],
+ "mgmt": [],
+ "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 "mgmtd" in result:
+ daemons.append("mgmtd")
+ 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.debug("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.debug("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.debug(
+ "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.debug("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.debug(
+ '\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.debug("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.debug(
+ "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.debug(
+ "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.debug(
+ '\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.debug("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.debug("Waiting on support bundle for %s", rname)
+ output, error = bundle_procs[rname].communicate()
+ if output:
+ logger.debug(
+ "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):
+ """
+ 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))
+
+ topo = tgen.json_topo
+ feature = set()
+
+ if "feature" in topo:
+ feature.update(topo["feature"])
+
+ if rname in topo["routers"]:
+ for key in topo["routers"][rname].keys():
+ feature.add(key)
+
+ for val in topo["routers"][rname]["links"].values():
+ if "pim" in val:
+ feature.add("pim")
+ break
+ for val in topo["routers"][rname]["links"].values():
+ if "pim6" in val:
+ feature.add("pim6")
+ break
+ for val in topo["routers"][rname]["links"].values():
+ if "ospf6" in val:
+ feature.add("ospf6")
+ break
+ if "switches" in topo and rname in topo["switches"]:
+ for val in topo["switches"][rname]["links"].values():
+ if "ospf" in val:
+ feature.add("ospf")
+ break
+ if "ospf6" in val:
+ feature.add("ospf6")
+ break
+
+ # Loading empty mgmtd.conf file to router, to start the mgmtd daemon
+ router.load_config(
+ TopoRouter.RD_MGMTD, "{}/{}/mgmtd.conf".format(tgen.logdir, rname)
+ )
+
+ # Loading empty zebra.conf file to router, to start the zebra deamon
+ router.load_config(
+ TopoRouter.RD_ZEBRA, "{}/{}/zebra.conf".format(tgen.logdir, rname)
+ )
+
+ # Loading empty bgpd.conf file to router, to start the bgp deamon
+ if "bgp" in feature:
+ router.load_config(
+ TopoRouter.RD_BGP, "{}/{}/bgpd.conf".format(tgen.logdir, rname)
+ )
+
+ # Loading empty pimd.conf file to router, to start the pim deamon
+ if "pim" in feature:
+ router.load_config(
+ TopoRouter.RD_PIM, "{}/{}/pimd.conf".format(tgen.logdir, rname)
+ )
+
+ # Loading empty pimd.conf file to router, to start the pim deamon
+ if "pim6" in feature:
+ router.load_config(
+ TopoRouter.RD_PIM6, "{}/{}/pim6d.conf".format(tgen.logdir, rname)
+ )
+
+ if "ospf" in feature:
+ # Loading empty ospf.conf file to router, to start the ospf deamon
+ router.load_config(
+ TopoRouter.RD_OSPF, "{}/{}/ospfd.conf".format(tgen.logdir, rname)
+ )
+
+ if "ospf6" in feature:
+ # Loading empty ospf.conf file to router, to start the ospf deamon
+ router.load_config(
+ TopoRouter.RD_OSPF6, "{}/{}/ospf6d.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.debug("[DUT: %s]: Running command: %s", dut, cmd)
+ result = rnode.run(cmd)
+ logger.debug("result %s", result)
+
+ # Bringing interface up
+ cmd = "ip link set {} up".format(vlan_intf)
+ logger.debug("[DUT: %s]: Running command: %s", dut, cmd)
+ result = rnode.run(cmd)
+ logger.debug("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.debug("[DUT: %s]: Running command: %s", dut, cmd)
+ result = rnode.run(cmd)
+ logger.debug("result %s", result)
+
+
+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.debug(
+ "[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.debug(
+ "[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.debug(
+ "[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.debug(
+ "[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.debug(
+ "[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.debug("[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.debug("[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.debug("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.debug(
+ "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.debug(
+ "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.
+ """
+ if bool(topotest.g_pytest_config.get_option("--pause")):
+ pause_test("before :" + msg)
+ _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"]
+ )
+ interface_data.append("exit")
+ 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", None)
+
+ # 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
+
+
+#############################################
+# 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..042ed09
--- /dev/null
+++ b/tests/topotests/lib/fixtures.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 eval: (yapf-mode 1) -*-
+# SPDX-License-Identifier: MIT
+#
+# August 27 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C. ("LabN")
+
+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..8c4701c
--- /dev/null
+++ b/tests/topotests/lib/grpc-query.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: MIT
+#
+# February 22 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+
+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
+
+ sys.path.append(os.path.dirname(CWD))
+ from munet.base import commander
+
+ commander.cmd_raises(f"cp {CWD}/../../../grpc/frr-northbound.proto .")
+ commander.cmd_raises(
+ f"python3 -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
+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..e897a81
--- /dev/null
+++ b/tests/topotests/lib/ltemplate.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..1eb88f2
--- /dev/null
+++ b/tests/topotests/lib/lutil.py
@@ -0,0 +1,499 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# Copyright 2017, LabN Consulting, L.L.C.
+
+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
+ l_wait_strict = 1
+
+ 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,
+ force_result=False,
+ ):
+ 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 or force_result 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()
+
+ if (op == "wait-strict") or ((op == "wait") and self.l_wait_strict):
+ strict = True
+ else:
+ strict = False
+
+ # Calculate the amount of `sleep`s we are going to peform.
+ wait_count = int(math.ceil(wait / wait_time)) + 1
+
+ force_result = False
+ while wait_count > 0:
+ n += 1
+
+ # log a failure on last iteration if we don't get desired regexp
+ if strict and (wait_count == 1):
+ force_result = True
+
+ found = self.command(
+ target, command, regexp, op, result, returnJson, startt, force_result
+ )
+ 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,
+):
+ waitops = ["wait", "wait-strict", "wait-nostrict"]
+
+ if op in waitops:
+ return LUtil.wait(
+ target, command, regexp, op, result, time, returnJson, wait_time
+ )
+ else:
+ return LUtil.command(target, command, regexp, op, result, returnJson)
+
+
+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)
+
+
+#
+# Sets default wait type for luCommand(op="wait) (may be overridden by
+# specifying luCommand(op="wait-strict") or luCommand(op="wait-nostrict")).
+#
+# "nostrict" is the historical default behavior, which is to ignore
+# failures to match the specified regexp in the specified time.
+#
+# "strict" means that failure to match the specified regexp in the
+# specified time yields an explicit, logged failure result
+#
+def luSetWaitType(waittype):
+ if waittype == "strict":
+ LUtil.l_wait_strict = 1
+ else:
+ if waittype == "nostrict":
+ LUtil.l_wait_strict = 0
+ else:
+ raise ValueError('waittype must be one of "strict" or "nostrict"')
+
+
+# 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..5efbecd
--- /dev/null
+++ b/tests/topotests/lib/mcast-tester.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: ISC
+#
+# Copyright (C) 2021 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+
+"""
+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 ipaddress
+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."
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+
+ if ip_version == 4:
+ mreq = group.packed + struct.pack("@II", socket.INADDR_ANY, ifindex)
+ opt = socket.IP_ADD_MEMBERSHIP
+ else:
+ mreq = group.packed + struct.pack("@I", ifindex)
+ opt = socket.IPV6_JOIN_GROUP
+ sock.bind((str(group), port))
+ sock.setsockopt(ip_proto, opt, 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("--port", type=int, default=1000, help="port to send to")
+parser.add_argument("--ttl", type=int, default=16, help="TTL/hops for sending packets")
+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()
+
+# 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)
+
+args.group = ipaddress.ip_address(args.group)
+ip_version = args.group.version
+ip_family = socket.AF_INET if ip_version == 4 else socket.AF_INET6
+ip_proto = socket.IPPROTO_IP if ip_version == 4 else socket.IPPROTO_IPV6
+
+msock = socket.socket(ip_family, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+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/hops.
+ ttlopt = socket.IP_MULTICAST_TTL if ip_version == 4 else socket.IPV6_MULTICAST_HOPS
+ if ip_version == 4:
+ msock.setsockopt(ip_proto, ttlopt, struct.pack("B", args.ttl))
+ else:
+ msock.setsockopt(ip_proto, ttlopt, struct.pack("I", args.ttl))
+
+ # Block to ensure packet send.
+ msock.setblocking(True)
+else:
+ multicast_join(msock, ifindex, args.group, args.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, (str(args.group), args.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..f4aa827
--- /dev/null
+++ b/tests/topotests/lib/micronet.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# July 9 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021-2023, LabN Consulting, L.L.C.
+#
+# flake8: noqa
+
+from munet.base import BaseMunet as Micronet
+from munet.base import (
+ Bridge,
+ Commander,
+ LinuxNamespace,
+ SharedNamespace,
+ Timeout,
+ cmd_error,
+ comm_error,
+ commander,
+ get_exec_path,
+ proc_error,
+ root_hostname,
+ shell_quote,
+)
diff --git a/tests/topotests/lib/micronet_compat.py b/tests/topotests/lib/micronet_compat.py
new file mode 100644
index 0000000..b348c85
--- /dev/null
+++ b/tests/topotests/lib/micronet_compat.py
@@ -0,0 +1,370 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# July 11 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021-2023, LabN Consulting, L.L.C
+#
+import ipaddress
+import os
+
+from munet import cli
+from munet.base import BaseMunet, LinuxNamespace
+
+
+class Node(LinuxNamespace):
+ """Node (mininet compat)."""
+
+ def __init__(self, name, rundir=None, **kwargs):
+ nkwargs = {}
+
+ if "unet" in kwargs:
+ nkwargs["unet"] = kwargs["unet"]
+ if "private_mounts" in kwargs:
+ nkwargs["private_mounts"] = kwargs["private_mounts"]
+ if "logger" in kwargs:
+ nkwargs["logger"] = kwargs["logger"]
+
+ # This is expected by newer munet CLI code
+ self.config_dirname = ""
+ self.config = {"kind": "frr"}
+ self.mgmt_ip = None
+ self.mgmt_ip6 = None
+
+ super().__init__(name, **nkwargs)
+
+ self.rundir = self.unet.rundir.joinpath(self.name)
+
+ def cmd(self, cmd, **kwargs):
+ """Execute a command, joins stdout, stderr, ignores exit status."""
+
+ return super(Node, self).cmd_legacy(cmd, **kwargs)
+
+ def config_host(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
+
+ 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"])
+
+ set_cwd = LinuxNamespace.set_ns_cwd
+
+
+class Topo(object): # pylint: disable=R0205
+ def __init__(self, *args, **kwargs):
+ raise Exception("Remove Me")
+
+
+class Mininet(BaseMunet):
+ """
+ Mininet using Micronet.
+ """
+
+ g_mnet_inst = None
+
+ def __init__(self, rundir=None, pytestconfig=None, logger=None):
+ """
+ Create a Micronet.
+ """
+ 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__(
+ pid=False, rundir=rundir, pytestconfig=pytestconfig, logger=logger
+ )
+
+ # From munet/munet/native.py
+ with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f:
+ f.write(f"{self.pid}\n")
+
+ with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f:
+ f.write(f'{" ".join([str(x) for x in self.pids])}\n')
+
+ hosts_file = os.path.join(self.rundir, "hosts.txt")
+ with open(hosts_file, "w", encoding="ascii") as hf:
+ hf.write(
+ f"""127.0.0.1\tlocalhost {self.name}
+::1\tip6-localhost ip6-loopback
+fe00::0\tip6-localnet
+ff00::0\tip6-mcastprefix
+ff02::1\tip6-allnodes
+ff02::2\tip6-allrouters
+"""
+ )
+ self.bind_mount(hosts_file, "/etc/hosts")
+
+ # Common CLI commands for any topology
+ cdict = {
+ "commands": [
+ #
+ # Window commands.
+ #
+ {
+ "name": "pcap",
+ "format": "pcap NETWORK",
+ "help": (
+ "capture packets from NETWORK into file capture-NETWORK.pcap"
+ " the command is run within a new window which also shows"
+ " packet summaries. NETWORK can also be an interface specified"
+ " as HOST:INTF. To capture inside the host namespace."
+ ),
+ "exec": "tshark -s 9200 -i {0} -P -w capture-{0}.pcap",
+ "top-level": True,
+ "new-window": {"background": True},
+ },
+ {
+ "name": "term",
+ "format": "term HOST [HOST ...]",
+ "help": "open terminal[s] (TMUX or XTerm) on HOST[S], * for all",
+ "exec": "bash",
+ "new-window": True,
+ },
+ {
+ "name": "vtysh",
+ "exec": "/usr/bin/vtysh",
+ "format": "vtysh ROUTER [ROUTER ...]",
+ "new-window": True,
+ "kinds": ["frr"],
+ },
+ {
+ "name": "xterm",
+ "format": "xterm HOST [HOST ...]",
+ "help": "open XTerm[s] on HOST[S], * for all",
+ "exec": "bash",
+ "new-window": {
+ "forcex": True,
+ },
+ },
+ {
+ "name": "logd",
+ "exec": "tail -F %RUNDIR%/{}.log",
+ "format": "logd HOST [HOST ...] DAEMON",
+ "help": (
+ "tail -f on the logfile of the given "
+ "DAEMON for the given HOST[S]"
+ ),
+ "new-window": True,
+ },
+ {
+ "name": "stdlog",
+ "exec": (
+ "[ -e %RUNDIR%/frr.log ] && tail -F %RUNDIR%/frr.log "
+ "|| tail -F /var/log/frr.log"
+ ),
+ "format": "stdlog HOST [HOST ...]",
+ "help": "tail -f on the `frr.log` for the given HOST[S]",
+ "new-window": True,
+ },
+ {
+ "name": "stdout",
+ "exec": "tail -F %RUNDIR%/{0}.err",
+ "format": "stdout HOST [HOST ...] DAEMON",
+ "help": (
+ "tail -f on the stdout of the given DAEMON for the given HOST[S]"
+ ),
+ "new-window": True,
+ },
+ {
+ "name": "stderr",
+ "exec": "tail -F %RUNDIR%/{0}.out",
+ "format": "stderr HOST [HOST ...] DAEMON",
+ "help": (
+ "tail -f on the stderr of the given DAEMON for the given HOST[S]"
+ ),
+ "new-window": True,
+ },
+ #
+ # Non-window commands.
+ #
+ {
+ "name": "",
+ "exec": "vtysh -c '{}'",
+ "format": "[ROUTER ...] COMMAND",
+ "help": "execute vtysh COMMAND on the router[s]",
+ "kinds": ["frr"],
+ },
+ {
+ "name": "sh",
+ "format": "[HOST ...] sh <SHELL-COMMAND>",
+ "help": "execute <SHELL-COMMAND> on hosts",
+ "exec": "{}",
+ },
+ {
+ "name": "shi",
+ "format": "[HOST ...] shi <INTERACTIVE-COMMAND>",
+ "help": "execute <INTERACTIVE-COMMAND> on HOST[s]",
+ "exec": "{}",
+ "interactive": True,
+ },
+ ]
+ }
+
+ cli.add_cli_config(self, cdict)
+
+ shellopt = self.cfgopt.get_option_list("--shell")
+ if "all" in shellopt or "." in shellopt:
+ self.run_in_window("bash", title="munet")
+
+ # This is expected by newer munet CLI code
+ self.config_dirname = ""
+ self.config = {}
+
+ 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))
+
+ # can be used by munet cli
+ host.mgmt_ip = ipaddress.ip_address(ip)
+
+ if "defaultRoute" in params:
+ host.cmd_raises(
+ "ip route add default {}".format(params["defaultRoute"])
+ )
+
+ host.config_host()
+
+ 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."""
+ pcapopt = self.cfgopt.get_option_list("--pcap")
+ if "all" in pcapopt:
+ pcapopt = self.switches.keys()
+ for pcap in pcapopt:
+ if ":" in pcap:
+ host, intf = pcap.split(":")
+ pcap = f"{host}-{intf}"
+ host = self.hosts[host]
+ else:
+ host = self
+ intf = pcap
+ pcapfile = f"{self.rundir}/capture-{pcap}.pcap"
+ host.run_in_window(
+ f"tshark -s 9200 -i {intf} -P -w {pcapfile}",
+ background=True,
+ title=f"cap:{pcap}",
+ )
+
+ 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.cli(self)
diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py
new file mode 100644
index 0000000..5b18f8b
--- /dev/null
+++ b/tests/topotests/lib/ospf.py
@@ -0,0 +1,3028 @@
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+import ipaddress
+import sys
+from copy import deepcopy
+from time import sleep
+
+# 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)
+
+ # Flood reduction.
+ flood_data = ospf_data.setdefault("flood-reduction", {})
+ if flood_data:
+ cmd = "flood-reduction"
+ del_action = ospf_data.setdefault("del_flood_reduction", False)
+ if del_action:
+ cmd = "no flood-reduction"
+ config_data.append(cmd)
+
+ # LSA refresh timer - A hidden command.
+ refresh_data = ospf_data.setdefault("lsa-refresh", {})
+ if refresh_data:
+ cmd = "ospf lsa-refresh {}".format(refresh_data)
+ del_action = ospf_data.setdefault("del_lsa_refresh", False)
+ if del_action:
+ cmd = "no ospf lsa-refresh"
+ 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"])
+
+ if "flood-reduction" in area:
+ cmd = cmd + " flood-reduction"
+
+ 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)
+
+ config_data.append("exit")
+ 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"
+ elif data_ospf_auth == "key-chain":
+ cmd = "ip ospf authentication key-chain {}".format(
+ ospf_data["keychain"]
+ )
+ 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": {
+ "nbrState": "Full",
+ "role": "DR"
+ },
+ "r2": {
+ "nbrState": "Full",
+ "role": "DROther"
+ },
+ "r3": {
+ "nbrState": "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]["nbrState"].split("/")[0]
+ intf_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[1]
+ except KeyError:
+ errormsg = "[DUT: {}] OSPF peer {} missing".format(router, nbr_rid)
+ return errormsg
+
+ nbr_state = nbr_data.setdefault("nbrState", 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"]
+ logger.info("ospf neighbor %s: router-id: %s", router, data_rid)
+ 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]["nbrState"].split("/")[0]
+ except KeyError:
+ errormsg = (
+ "[DUT: {}] missing OSPF neighbor {} with router-id {}".format(
+ router, ospf_nbr, nbr_rid
+ )
+ )
+ 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
+
+
+@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"
+ ]
+ logger.info("ospf neighbor %s: router-id: %s", ospf_nbr, data_rid)
+ 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: {}] missing OSPF neighbor {} with router-id {}".format(
+ router, ospf_nbr, nbr_rid
+ )
+ )
+ 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=40)
+def verify_ospf_database(
+ tgen, topo, dut, input_dict, vrf=None, lsatype=None, rid=None, 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)
+
+ if not rid:
+ rid = "self-originate"
+ if lsatype:
+ if vrf is None:
+ command = "show ip ospf database {} {} json".format(lsatype, rid)
+ else:
+ command = "show ip ospf database {} {} vrf {} json".format(
+ lsatype, rid, vrf
+ )
+ else:
+ if vrf is None:
+ command = "show ip ospf database json"
+ else:
+ command = "show ip ospf database vrf {} json".format(vrf)
+
+ show_ospf_json = run_frr_cmd(rnode, command, 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)
+ # import pdb; pdb.set_trace()
+ if ospf_db_data:
+ for ospf_area, area_lsa in ospf_db_data.items():
+ if ospf_area in show_ospf_json["routerLinkStates"]["areas"]:
+ if "routerLinkStates" in area_lsa:
+ for lsa in area_lsa["routerLinkStates"]:
+ _advrtr = lsa.setdefault("advertisedRouter", None)
+ _options = lsa.setdefault("options", None)
+
+ if (
+ _options
+ and lsa["lsaId"]
+ == show_ospf_json["routerLinkStates"]["areas"][ospf_area][
+ 0
+ ]["linkStateId"]
+ and lsa["options"]
+ == show_ospf_json["routerLinkStates"]["areas"][ospf_area][
+ 0
+ ]["options"]
+ ):
+ result = True
+ break
+ else:
+ errormsg = '[DUT: {}] OSPF LSA options: expected {}, Received Options are {} lsa["options"] {} OSPF LSAID: expected lsaid {}, Received lsaid {}'.format(
+ dut,
+ show_ospf_json["routerLinkStates"]["areas"][ospf_area][
+ 0
+ ]["options"],
+ _options,
+ lsa["options"],
+ show_ospf_json["routerLinkStates"]["areas"][ospf_area][
+ 0
+ ]["linkStateId"],
+ lsa["lsaId"],
+ )
+ 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": {
+ "summaryAddress": "11.0.0.0/8",
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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]["summaryAddress"]
+
+ 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",
+ "restartSupport":"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
+
+
+def get_ospf_database(tgen, topo, dut, input_dict, vrf=None, lsatype=None, rid=None):
+ """
+ This API is to return 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
+ * `vrf` : vrf to be checked
+ * `lsatype` : type of lsa to be checked
+ * `rid` : router id for lsa to be checked
+ 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 = get_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))
+ sleep(10)
+ 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)
+ if not rid:
+ rid = "self-originate"
+ if lsatype:
+ if vrf is None:
+ command = "show ip ospf database {} {} json".format(lsatype, rid)
+ else:
+ command = "show ip ospf database {} {} vrf {} json".format(
+ lsatype, rid, vrf
+ )
+ else:
+ if vrf is None:
+ command = "show ip ospf database json"
+ else:
+ command = "show ip ospf database vrf {} json".format(vrf)
+
+ show_ospf_json = run_frr_cmd(rnode, command, 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 "areas" in show_ospf_json and 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"
+ ]:
+ _advrtr = lsa.setdefault("advertisedRouter", None)
+ _options = lsa.setdefault("options", None)
+ if (
+ _advrtr
+ and lsa["lsaId"] == rtrlsa["lsaId"]
+ and lsa["advertisedRouter"]
+ == rtrlsa["advertisedRouter"]
+ ):
+ result = True
+ break
+ if (
+ _options
+ and lsa["lsaId"] == rtrlsa["lsaId"]
+ and lsa["options"] == rtrlsa["options"]
+ ):
+ 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 {}\n found Router LSA: {}".format(
+ router, ospf_area, lsa, rtrlsa
+ )
+ )
+ 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
+ else:
+ if "routerLinkStates" in area_lsa:
+ for lsa in area_lsa["routerLinkStates"]:
+ for rtrlsa in show_ospf_json["routerLinkStates"]:
+ _advrtr = lsa.setdefault("advertisedRouter", None)
+ _options = lsa.setdefault("options", None)
+ _age = lsa.setdefault("lsaAge", None)
+ if (
+ _options
+ and lsa["options"]
+ == show_ospf_json["routerLinkStates"][rtrlsa][
+ ospf_area
+ ][0]["options"]
+ ):
+ result = True
+ break
+ if (
+ _age != "get"
+ and lsa["lsaAge"]
+ == show_ospf_json["routerLinkStates"][rtrlsa][
+ ospf_area
+ ][0]["lsaAge"]
+ ):
+ result = True
+ break
+
+ if _age == "get":
+ return "{}".format(
+ show_ospf_json["routerLinkStates"][rtrlsa][
+ ospf_area
+ ][0]["lsaAge"]
+ )
+ 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 {}\n found Router LSA: {}".format(
+ router,
+ ospf_area,
+ lsa,
+ show_ospf_json["routerLinkStates"],
+ )
+ )
+ 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
diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py
new file mode 100644
index 0000000..f7440ef
--- /dev/null
+++ b/tests/topotests/lib/pim.py
@@ -0,0 +1,5203 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+
+import datetime
+import functools
+import os
+import re
+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 (
+ HostApplicationHelper,
+ InvalidCLIError,
+ create_common_configuration,
+ create_common_configurations,
+ get_frr_ipv6_linklocal,
+ 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
+
+from lib import topotest
+
+####
+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/pim6 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"]
+
+ # pim6
+ 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 pim6 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 "pim" in input_dict[router]:
+ if "disable" in input_dict[router]["pim"]:
+ enable_flag = False
+ interfaces = input_dict[router]["pim"]["disable"]
+
+ if type(interfaces) is not list:
+ interfaces = [interfaces]
+
+ for interface in interfaces:
+ cmd = "interface {}".format(interface)
+ config_data.append(cmd)
+ config_data.append("no 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")
+
+ if "pim6" in input_dict[router]:
+ if "disable" in input_dict[router]["pim6"]:
+ enable_flag = False
+ interfaces = input_dict[router]["pim6"]["disable"]
+
+ if type(interfaces) is not list:
+ interfaces = [interfaces]
+
+ for interface in interfaces:
+ cmd = "interface {}".format(interface)
+ config_data.append(cmd)
+ config_data.append("no 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=12)
+def verify_pim6_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None, expected=True):
+ """
+ Verify all pim6 neighbors are up and running, config is verified
+ using "show ipv6 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_pim6_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 ipv6 pim neighbor json", isjson=True
+ )
+
+ for destLink, data in topo["routers"][router]["links"].items():
+ if "type" in data and data["type"] == "loopback":
+ continue
+
+ if iface is not None and iface != data["interface"]:
+ continue
+
+ if "pim6" not in data:
+ continue
+
+ if "pim6" in data and data["pim6"] == "disable":
+ continue
+
+ if "pim6" in data and data["pim6"] == "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]
+ peer_interface = data["interface"]
+ if "type" in data and data["type"] == "loopback":
+ continue
+
+ if "pim6" not in data:
+ continue
+
+ logger.info("[DUT: %s]: Verifying PIM neighbor status:", router)
+
+ if "pim6" in data and data["pim6"] == "enable":
+ pim_nh_intf_ip = get_frr_ipv6_linklocal(tgen, destLink, peer_interface)
+
+ # 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, PIM6"
+ " 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 PIM6 neighbor %s",
+ router,
+ local_interface,
+ pim_nh_intf_ip,
+ )
+ else:
+ errormsg = (
+ "[DUT %s]: Local interface: %s, and"
+ "interface ip: %s is not found in "
+ "PIM6 neighbor " % (router, local_interface, pim_nh_intf_ip)
+ )
+ return errormsg
+ else:
+ errormsg = (
+ "[DUT %s]: Local interface: %s, is not "
+ "present in PIM6 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=2)
+def verify_upstream_iif(
+ tgen,
+ dut,
+ iif,
+ src_address,
+ group_addresses,
+ joinState=None,
+ regState=None,
+ refCount=1,
+ addr_type="ipv4",
+ 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/MLD 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) and joinState "
+ "for (%s, %s), Expected iif: %s, "
+ "Found iif : %s, and Expected "
+ "joinState :%s , Found joinState: %s"
+ % (
+ dut,
+ src_address,
+ grp_addr,
+ in_interface,
+ group_addr_json[src_address]["inboundInterface"],
+ "Joined",
+ group_addr_json[src_address]["joinState"],
+ )
+ )
+ return errormsg
+
+ elif group_addr_json[src_address]["joinState"] != joinState:
+ errormsg = (
+ "[DUT %s]: Verifying iif "
+ "(Inbound Interface) and joinState "
+ "for (%s, %s), Expected iif: %s, "
+ "Found iif : %s, and Expected "
+ "joinState :%s , Found joinState: %s"
+ % (
+ dut,
+ src_address,
+ grp_addr,
+ in_interface,
+ group_addr_json[src_address]["inboundInterface"],
+ joinState,
+ group_addr_json[src_address]["joinState"],
+ )
+ )
+ return errormsg
+
+ if regState:
+ if group_addr_json[src_address]["regState"] != regState:
+ errormsg = (
+ "[DUT %s]: Verifying iif "
+ "(Inbound Interface) and regState "
+ "for (%s, %s), Expected iif: %s, "
+ "Found iif : %s, and Expected "
+ "regState :%s , Found regState: %s"
+ % (
+ dut,
+ src_address,
+ grp_addr,
+ in_interface,
+ group_addr_json[src_address]["inboundInterface"],
+ regState,
+ group_addr_json[src_address]["regState"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: Verifying iif(Inbound Interface)"
+ " for (%s,%s) and joinState is %s regstate is %s [PASSED]!! "
+ " Found Expected: (%s)",
+ dut,
+ src_address,
+ grp_addr,
+ group_addr_json[src_address]["joinState"],
+ group_addr_json[src_address]["regState"],
+ 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, addr_type="ipv4", 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,
+ addr_type="ipv4",
+ 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,
+ addr_type="ipv4",
+ 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,
+ addr_type="ipv4",
+ 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 traffic 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
+
+
+def get_pim6_interface_traffic(tgen, input_dict):
+ """
+ get ipv6 pim interface traffic by running
+ "show ipv6 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 ipv6 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, addr_type="ipv4", 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]
+
+ if addr_type == "ipv4":
+ addr_cmd = "ip"
+ pim_cmd = "pim"
+ elif addr_type == "ipv6":
+ addr_cmd = "ipv6"
+ pim_cmd = "pim6"
+ show_pim_interface_json = rnode.vtysh_cmd(
+ "show {} pim interface json".format(addr_cmd), isjson=True
+ )
+
+ logger.info("show_pim_interface_json: \n %s", show_pim_interface_json)
+
+ if interface_ip:
+ if interface in show_pim_interface_json:
+ pim_intf_json = show_pim_interface_json[interface]
+ if pim_intf_json["address"] != interface_ip:
+ errormsg = (
+ "[DUT %s]: %s interface "
+ "%s is not correct "
+ "[FAILED]!! Expected : %s, Found : %s"
+ % (
+ dut,
+ pim_cmd,
+ addr_cmd,
+ pim_intf_json["address"],
+ interface_ip,
+ )
+ )
+ return errormsg
+ else:
+ logger.info(
+ "[DUT %s]: %s interface "
+ "%s is correct "
+ "[Passed]!! Expected : %s, Found : %s"
+ % (
+ dut,
+ pim_cmd,
+ addr_cmd,
+ 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_cmd in data and data[pim_cmd] == "enable":
+ pim_interface = data["interface"]
+ pim_intf_ip = data[addr_type].split("/")[0]
+
+ if pim_interface in show_pim_interface_json:
+ pim_intf_json = show_pim_interface_json[pim_interface]
+ else:
+ errormsg = (
+ "[DUT %s]: %s interface: %s "
+ "PIM interface %s: %s, not Found"
+ % (dut, pim_cmd, pim_interface, addr_cmd, 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]: %s interface: %s "
+ "PIM interface %s: %s, status check "
+ "[FAILED]!! Expected : %s, Found : %s"
+ % (
+ dut,
+ pim_cmd,
+ pim_interface,
+ addr_cmd,
+ pim_intf_ip,
+ pim_interface,
+ pim_intf_json["state"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: %s interface: %s, "
+ "interface %s: %s, status: %s"
+ " [PASSED]!!",
+ dut,
+ pim_cmd,
+ pim_interface,
+ addr_cmd,
+ 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 pim interface traffic by running
+ "clear ip 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_pim6_interface_traffic(tgen, topo):
+ """
+ Clear ipv6 pim interface traffic by running
+ "clear ipv6 pim interface traffic" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ Usage
+ -----
+
+ result = clear_pim6_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 pim6 interface traffic", dut)
+ result = run_frr_cmd(rnode, "clear ipv6 pim interface traffic")
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+def clear_pim6_interfaces(tgen, topo):
+ """
+ Clear ipv6 pim interface by running
+ "clear ipv6 pim interface" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ Usage
+ -----
+
+ result = clear_pim6_interfaces(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 pim6 interfaces", dut)
+ result = run_frr_cmd(rnode, "clear ipv6 pim interface")
+
+ 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):
+ sleep(5)
+ logger.info("[DUT: %s]: Waiting for 5 sec for PIM neighbors" " to come up", dut)
+ 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 clear_pim6_mroute(tgen, dut=None):
+ """
+ Clear ipv6 mroute by running "clear ipv6 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 ipv6 mroute", router)
+ rnode.vtysh_cmd("clear ipv6 mroute")
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+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,
+ addr_type="ipv4",
+ 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)
+
+ 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"
+
+ show_pim_join_json = run_frr_cmd(
+ rnode, "show {} pim join json".format(ip_cmd), isjson=True
+ )
+
+ 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, 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 traffic 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
+
+
+@retry(retry_timeout=40, diag_pct=0)
+def verify_mld_groups(tgen, dut, interface, group_addresses, expected=True):
+ """
+ Verify IGMP groups are received from an intended interface
+ by running "show ip mld groups" command
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+ * `interface`: interface, from which MLD groups would be received
+ * `group_addresses`: MLD group address
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ dut = "r1"
+ interface = "r1-r0-eth0"
+ group_address = "ffaa::1"
+ result = verify_mld_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 mld groups received:", dut)
+ show_mld_json = run_frr_cmd(rnode, "show ipv6 mld groups json", isjson=True)
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ if interface in show_mld_json:
+ show_mld_json = show_mld_json[interface]["groups"]
+ else:
+ errormsg = (
+ "[DUT %s]: Verifying MLD group received"
+ " from interface %s [FAILED]!! " % (dut, interface)
+ )
+ return errormsg
+
+ found = False
+ for grp_addr in group_addresses:
+ for index in show_mld_json:
+ if index["group"] == grp_addr:
+ found = True
+ break
+ if found is not True:
+ errormsg = (
+ "[DUT %s]: Verifying MLD group received"
+ " from interface %s [FAILED]!! "
+ " Expected not found: %s" % (dut, interface, grp_addr)
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: Verifying MLD 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=40, diag_pct=0)
+def verify_mld_interface(tgen, dut, mld_iface, interface_ip, expected=True):
+ """
+ Verify all IGMP interface are up and running, config is verified
+ using "show ip mld interface" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo` : json file data
+ * `dut` : device under test
+ * `mld_iface` : interface name
+ * `interface_ip` : interface ip address
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ result = verify_mld_interface(tgen, topo, dut, mld_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 MLD interface status:", dut)
+
+ rnode = tgen.routers()[dut]
+ show_mld_interface_json = run_frr_cmd(
+ rnode, "show ipv6 mld interface json", isjson=True
+ )
+
+ if mld_iface in show_mld_interface_json:
+ mld_intf_json = show_mld_interface_json[mld_iface]
+ # Verifying igmp interface
+ if mld_intf_json["address"] != interface_ip:
+ errormsg = (
+ "[DUT %s]: igmp interface ip is not correct "
+ "[FAILED]!! Expected : %s, Found : %s"
+ % (dut, mld_intf_json["address"], interface_ip)
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: igmp interface: %s, " "interface ip: %s" " [PASSED]!!",
+ dut,
+ mld_iface,
+ interface_ip,
+ )
+ else:
+ errormsg = (
+ "[DUT %s]: igmp interface: %s "
+ "igmp interface ip: %s, is not present "
+ % (dut, mld_iface, interface_ip)
+ )
+ 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_mld_config(tgen, input_dict, stats_return=False, expected=True):
+ """
+ Verify mld 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": {
+ "mld": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "mld": {
+ "query": {
+ "query-interval" : 200,
+ "query-max-response-time" : 100
+ },
+ "statistics": {
+ "queryV2" : 2,
+ "reportV2" : 1
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = verify_mld_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]["mld"]["interfaces"].items():
+ statistics = False
+ report = False
+ if "statistics" in input_dict[dut]["mld"]["interfaces"][interface]["mld"]:
+ statistics = True
+ cmd = "show ipv6 mld statistics"
+ else:
+ cmd = "show ipv6 mld"
+
+ logger.info("[DUT: %s]: Verifying MLD interface %s detail:", dut, interface)
+
+ if statistics:
+ if (
+ "report"
+ in input_dict[dut]["mld"]["interfaces"][interface]["mld"][
+ "statistics"
+ ]
+ ):
+ report = True
+
+ if statistics and report:
+ show_ipv6_mld_intf_json = run_frr_cmd(
+ rnode, "{} json".format(cmd), isjson=True
+ )
+ intf_detail_json = show_ipv6_mld_intf_json["global"]
+ else:
+ show_ipv6_mld_intf_json = run_frr_cmd(
+ rnode, "{} interface {} json".format(cmd, interface), isjson=True
+ )
+
+ show_ipv6_mld_intf_json = show_ipv6_mld_intf_json["default"]
+
+ if not report:
+ if interface not in show_ipv6_mld_intf_json:
+ errormsg = (
+ "[DUT %s]: MLD interface: %s "
+ " is not present in CLI output "
+ "[FAILED]!! " % (dut, interface)
+ )
+ return errormsg
+
+ else:
+ intf_detail_json = show_ipv6_mld_intf_json[interface]
+
+ if stats_return:
+ mld_stats = {}
+
+ if "statistics" in data["mld"]:
+ if stats_return:
+ mld_stats["statistics"] = {}
+ for query, value in data["mld"]["statistics"].items():
+ if query == "queryV1":
+ # Verifying IGMP interface queryV2 statistics
+ if stats_return:
+ mld_stats["statistics"][query] = intf_detail_json["queryV1"]
+
+ else:
+ if intf_detail_json["queryV1"] != value:
+ errormsg = (
+ "[DUT %s]: MLD interface: %s "
+ " queryV1 statistics verification "
+ "[FAILED]!! Expected : %s,"
+ " Found : %s"
+ % (
+ dut,
+ interface,
+ value,
+ intf_detail_json["queryV1"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: MLD interface: %s "
+ "queryV1 statistics is %s",
+ dut,
+ interface,
+ value,
+ )
+
+ if query == "reportV1":
+ # Verifying IGMP interface timerV2 statistics
+ if stats_return:
+ mld_stats["statistics"][query] = intf_detail_json[
+ "reportV1"
+ ]
+
+ else:
+ if intf_detail_json["reportV1"] <= value:
+ errormsg = (
+ "[DUT %s]: MLD reportV1 "
+ "statistics verification "
+ "[FAILED]!! Expected : %s "
+ "or more, Found : %s"
+ % (
+ dut,
+ interface,
+ value,
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: MLD reportV1 " "statistics is %s",
+ dut,
+ intf_detail_json["reportV1"],
+ )
+
+ if "query" in data["mld"]:
+ for query, value in data["mld"]["query"].items():
+ if query == "query-interval":
+ # Verifying IGMP interface query interval timer
+ if intf_detail_json["timerQueryIntervalMsec"] != value * 1000:
+ errormsg = (
+ "[DUT %s]: MLD interface: %s "
+ " query-interval verification "
+ "[FAILED]!! Expected : %s,"
+ " Found : %s"
+ % (
+ dut,
+ interface,
+ value,
+ intf_detail_json["timerQueryIntervalMsec"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: MLD interface: %s " "query-interval is %s",
+ dut,
+ interface,
+ value * 1000,
+ )
+
+ if query == "query-max-response-time":
+ # Verifying IGMP interface query max response timer
+ if (
+ intf_detail_json["timerQueryResponseTimerMsec"]
+ != value * 100
+ ):
+ errormsg = (
+ "[DUT %s]: MLD interface: %s "
+ "query-max-response-time "
+ "verification [FAILED]!!"
+ " Expected : %s, Found : %s"
+ % (
+ dut,
+ interface,
+ value * 100,
+ intf_detail_json["timerQueryResponseTimerMsec"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: MLD 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]: MLD 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]: MLD 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["timerLastMemberQueryIntervalMsec"]
+ != value * 100
+ ):
+ errormsg = (
+ "[DUT %s]: MLD interface: %s "
+ "last-member-query-interval "
+ "verification [FAILED]!!"
+ " Expected : %s, Found : %s"
+ % (
+ dut,
+ interface,
+ value * 100,
+ intf_detail_json[
+ "timerLastMemberQueryIntervalMsec"
+ ],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: MLD interface: %s "
+ "last-member-query-interval is %s ms",
+ dut,
+ interface,
+ value * 100,
+ )
+
+ if "version" in data["mld"]:
+ # Verifying IGMP interface state is up
+ if intf_detail_json["state"] != "up":
+ errormsg = (
+ "[DUT %s]: MLD interface: %s "
+ " state: %s verification "
+ "[FAILED]!!" % (dut, interface, intf_detail_json["state"])
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: MLD 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 mld_stats
+
+
+@retry(retry_timeout=60, diag_pct=0)
+def verify_pim_nexthop(tgen, topo, dut, nexthop, addr_type="ipv4"):
+ """
+ Verify all PIM nexthop details using "show ip/ipv6 pim neighbor" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo` : json file data
+ * `dut` : dut info
+ * `nexthop` : nexthop ip/ipv6 address
+
+ Usage
+ -----
+ result = verify_pim_nexthop(tgen, topo, dut, nexthop)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ rnode = tgen.routers()[dut]
+
+ if addr_type == "ipv4":
+ ip_cmd = "ip"
+ elif addr_type == "ipv6":
+ ip_cmd = "ipv6"
+
+ cmd = "show {} pim nexthop".format(addr_type)
+ pim_nexthop = rnode.vtysh_cmd(cmd)
+
+ if nexthop in pim_nexthop:
+ logger.info("[DUT %s]: Expected nexthop: %s, Found", dut, nexthop)
+ return True
+ else:
+ errormsg = "[DUT %s]: Nexthop not found: %s" % (dut, nexthop)
+ 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_mroute_summary(
+ tgen, dut, sg_mroute=None, starg_mroute=None, total_mroute=None, addr_type="ipv4"
+):
+ """
+ Verify ip mroute summary has correct (*,g) (s,G) and total mroutes
+ by running "show ip mroutes summary json" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+ * `sg_mroute`: Number of installed (s,g) mroute
+ * `starg_mroute`: Number installed of (*,g) mroute
+ * `Total_mroute`: Total number of installed mroutes
+ * 'addr_type : IPv4 or IPv6 address
+ * `return_json`: Whether to return raw json data
+
+ Usage
+ -----
+ dut = "r1"
+ sg_mroute = "4000"
+ starg_mroute= "2000"
+ total_mroute = "6000"
+ addr_type=IPv4 or IPv6
+ result = verify_mroute_summary(tgen, dut, sg_mroute=None, starg_mroute=None,
+ total_mroute= None)
+ Returns
+ -------
+ errormsg 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 mroute summary", dut)
+
+ if addr_type == "ipv4":
+ ip_cmd = "ip"
+ elif addr_type == "ipv6":
+ ip_cmd = "ipv6"
+
+ cmd = "show {} mroute summary json".format(ip_cmd)
+ show_mroute_summary_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ if starg_mroute is not None:
+ if show_mroute_summary_json["wildcardGroup"]["installed"] != starg_mroute:
+ logger.error(
+ "Number of installed starg are: %s but expected: %s",
+ show_mroute_summary_json["wildcardGroup"]["installed"],
+ starg_mroute,
+ )
+ return False
+ logger.info(
+ "Number of installed starg routes are %s",
+ show_mroute_summary_json["wildcardGroup"]["installed"],
+ )
+
+ if sg_mroute is not None:
+ if show_mroute_summary_json["sourceGroup"]["installed"] != sg_mroute:
+ logger.error(
+ "Number of installed SG routes are: %s but expected: %s",
+ show_mroute_summary_json["sourceGroup"]["installed"],
+ sg_mroute,
+ )
+ return False
+ logger.info(
+ "Number of installed SG routes are %s",
+ show_mroute_summary_json["sourceGroup"]["installed"],
+ )
+
+ if total_mroute is not None:
+ if show_mroute_summary_json["totalNumOfInstalledMroutes"] != total_mroute:
+ logger.error(
+ "Total number of installed mroutes are: %s but expected: %s",
+ show_mroute_summary_json["totalNumOfInstalledMroutes"],
+ total_mroute,
+ )
+ return False
+ logger.info(
+ "Number of installed Total mroute are %s",
+ show_mroute_summary_json["totalNumOfInstalledMroutes"],
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=60, diag_pct=0)
+def verify_sg_traffic(tgen, dut, groups, src, addr_type="ipv4"):
+ """
+ Verify multicast traffic by running
+ "show ip mroute count json" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `groups`: igmp or mld groups where traffic needs to be verified
+
+ Usage
+ -----
+ result = verify_sg_traffic(tgen, "r1", igmp_groups/mld_groups, srcaddress)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying multicast " "SG traffic", dut)
+
+ if addr_type == "ipv4":
+ cmd = "show ip mroute count json"
+ elif addr_type == "ipv6":
+ cmd = "show ipv6 mroute count json"
+ # import pdb; pdb.set_trace()
+ show_mroute_sg_traffic_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ if bool(show_mroute_sg_traffic_json) is False:
+ errormsg = "[DUT %s]: Json output is empty" % (dut)
+ return errormsg
+
+ before_traffic = {}
+ after_traffic = {}
+
+ for grp in groups:
+ if grp not in show_mroute_sg_traffic_json:
+ errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % (
+ dut,
+ src,
+ grp,
+ )
+ if src not in show_mroute_sg_traffic_json[grp]:
+ errormsg = (
+ "[DUT %s]: Verifying source is not present in "
+ " %s [FAILED]!! " % (dut, src)
+ )
+ return errormsg
+
+ before_traffic[grp] = show_mroute_sg_traffic_json[grp][src]["packets"]
+
+ logger.info("Waiting for 10sec traffic to increament")
+ sleep(10)
+
+ show_mroute_sg_traffic_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ for grp in groups:
+ if grp not in show_mroute_sg_traffic_json:
+ errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % (
+ dut,
+ src,
+ grp,
+ )
+ if src not in show_mroute_sg_traffic_json[grp]:
+ errormsg = (
+ "[DUT %s]: Verifying source is not present in "
+ " %s [FAILED]!! " % (dut, src)
+ )
+ return errormsg
+
+ after_traffic[grp] = show_mroute_sg_traffic_json[grp][src]["packets"]
+
+ for grp in groups:
+ if after_traffic[grp] <= before_traffic[grp]:
+ errormsg = (
+ "[DUT %s]: Verifying igmp group %s source %s not increamenting traffic"
+ " [FAILED]!! " % (dut, grp, src)
+ )
+ return errormsg
+ else:
+ logger.info(
+ "[DUT %s]:igmp group %s source %s receiving traffic"
+ " [PASSED]!! " % (dut, grp, src)
+ )
+ result = True
+
+ return result
+
+
+@retry(retry_timeout=60, diag_pct=0)
+def verify_pim6_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": {
+ "mld": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "pim6": {
+ "drPriority" : 10,
+ "helloPeriod" : 5
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = verify_pim6_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]["pim6"]["interfaces"].items():
+ logger.info(
+ "[DUT: %s]: Verifying PIM6 interface %s detail:", dut, interface
+ )
+
+ show_ipv6_pim_intf_json = run_frr_cmd(
+ rnode, "show ipv6 pim interface {} json".format(interface), isjson=True
+ )
+
+ if interface not in show_ipv6_pim_intf_json:
+ errormsg = (
+ "[DUT %s]: PIM6 interface: %s "
+ " is not present in CLI output "
+ "[FAILED]!! " % (dut, interface)
+ )
+ return errormsg
+
+ intf_detail_json = show_ipv6_pim_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]: PIM6 interface: %s "
+ " helloPeriod verification "
+ "[FAILED]!! Expected : %s,"
+ " Found : %s"
+ % (dut, interface, value, intf_detail_json["helloPeriod"])
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: PIM6 interface: %s " "helloPeriod is %s",
+ dut,
+ interface,
+ value,
+ )
+
+ if config == "drPriority":
+ # Verifying PIM interface drPriority
+ if intf_detail_json["drPriority"] != value:
+ errormsg = (
+ "[DUT %s]: PIM6 interface: %s "
+ " drPriority verification "
+ "[FAILED]!! Expected : %s,"
+ " Found : %s"
+ % (dut, interface, value, intf_detail_json["drPriority"])
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: PIM6 interface: %s " "drPriority is %s",
+ dut,
+ interface,
+ value,
+ )
+
+ if config == "drAddress":
+ # Verifying PIM interface drAddress
+ if intf_detail_json["drAddress"] != value:
+ errormsg = (
+ "[DUT %s]: PIM6 interface: %s "
+ " drAddress verification "
+ "[FAILED]!! Expected : %s,"
+ " Found : %s"
+ % (dut, interface, value, intf_detail_json["drAddress"])
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: PIM6 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=62)
+def verify_local_mld_groups(tgen, dut, interface, group_addresses):
+ """
+ Verify local MLD groups are received from an intended interface
+ by running "show ipv6 mld join json" command
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+ * `interface`: interface, from which IGMP groups are configured
+ * `group_addresses`: MLD group address
+ Usage
+ -----
+ dut = "r1"
+ interface = "r1-r0-eth0"
+ group_address = "ffaa::1"
+ result = verify_local_mld_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 MLD groups received:", dut)
+ show_ipv6_local_mld_json = run_frr_cmd(
+ rnode, "show ipv6 mld join json", isjson=True
+ )
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ if interface not in show_ipv6_local_mld_json["default"]:
+ errormsg = (
+ "[DUT %s]: Verifying local MLD group received"
+ " from interface %s [FAILED]!! " % (dut, interface)
+ )
+ return errormsg
+
+ for grp_addr in group_addresses:
+ found = False
+ if grp_addr in show_ipv6_local_mld_json["default"][interface]:
+ found = True
+ break
+ if not found:
+ errormsg = (
+ "[DUT %s]: Verifying local MLD group received"
+ " from interface %s [FAILED]!! "
+ " Expected: %s " % (dut, interface, grp_addr)
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: Verifying local MLD 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 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..377146e
--- /dev/null
+++ b/tests/topotests/lib/scapy_sendpkt.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: MIT
+#
+# July 29 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C. ("LabN")
+#
+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..c11de64
--- /dev/null
+++ b/tests/topotests/lib/send_bsr_packet.py
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+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..e7cd657
--- /dev/null
+++ b/tests/topotests/lib/snmptest.py
@@ -0,0 +1,145 @@
+# SPDX-License-Identifier: ISC
+#
+# topogen.py
+# Library of helper functions for NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+#
+
+"""
+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, options=""):
+ self.community = community
+ self.version = version
+ self.router = router
+ self.iface = iface
+ self.options = options
+ 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} {3}".format(
+ self.version, self.community, self.options, 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..230c2bd
--- /dev/null
+++ b/tests/topotests/lib/test/test_json.py
@@ -0,0 +1,627 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_json.py
+# Tests for library function: json_cmp().
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..5e171b2
--- /dev/null
+++ b/tests/topotests/lib/test/test_run_and_expect.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_run_and_expect.py
+# Tests for library function: run_and_expect(_type)().
+#
+# Copyright (c) 2019 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..7b18daa
--- /dev/null
+++ b/tests/topotests/lib/test/test_version.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_version.py
+# Tests for library function: version_cmp().
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..4d935b9
--- /dev/null
+++ b/tests/topotests/lib/topogen.py
@@ -0,0 +1,1390 @@
+# SPDX-License-Identifier: ISC
+#
+# topogen.py
+# Library of helper functions for NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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 configparser
+import grp
+import inspect
+import json
+import logging
+import os
+import platform
+import pwd
+import re
+import shlex
+import subprocess
+import sys
+from collections import OrderedDict
+
+import lib.topolog as topolog
+from lib.micronet import Commander
+from lib.micronet_compat import Mininet
+from lib.topolog import logger
+from munet.testing.util import pause_test
+
+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("exabgp", logger=logging.getLogger("exabgp"))
+
+ 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(topotest.g_pytest_config.option.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(
+ rundir=self.logdir,
+ pytestconfig=topotest.g_pytest_config,
+ logger=topolog.get_logger("mu", log_level="debug"),
+ )
+
+ # Adjust the parent namespace
+ topotest.fix_netns_limits(self.net)
+
+ # 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_bmp_server(self, name, ip, defaultRoute, port=1789):
+ """Add the bmp collector gear"""
+ if name in self.gears:
+ raise KeyError("The bmp server already exists")
+
+ self.gears[name] = TopoBMPCollector(
+ self, name, ip=ip, defaultRoute=defaultRoute, port=port
+ )
+
+ 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 get_bmp_servers(self):
+ """
+ Retruns the bmp servers dictionnary (the key is the bmp server the
+ value is the bmp server object itself).
+ """
+ return self.get_gears(TopoBMPCollector)
+
+ 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.
+ """
+ pause = bool(self.net.cfgopt.get_option("--pause-at-end"))
+ pause = pause or bool(self.net.cfgopt.get_option("--pause"))
+ if pause:
+ try:
+ pause_test("Before MUNET delete")
+ except KeyboardInterrupt:
+ print("^C...continuing")
+ except Exception as error:
+ self.logger.error("\n...continuing after error: %s", error)
+
+ 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 is 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("setting error msg: %s", 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_MGMTD = 20
+ 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",
+ RD_MGMTD: "mgmtd",
+ }
+
+ 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 "private_mounts" not in params:
+ params["private_mounts"] = 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
+ """
+ source_path = 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_path)
+ else:
+ grep_cmd = "grep 'router {}' {}".format(daemonstr, source_path)
+ result = self.run(grep_cmd, warn=False).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, TopoRouter.RD_MGMTD.
+
+ 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.debug('loading "{}" configuration: {}'.format(daemonstr, source))
+ return 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 6",
+ ]
+ ),
+ 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 6",
+ ]
+ ),
+ 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, shlex.quote(command)
+ )
+
+ self.logger.debug("vtysh command => {}".format(shlex.quote(command)))
+ output = self.run(vtysh_command)
+
+ dbgout = output.strip()
+ if dbgout:
+ if "\n" in dbgout:
+ dbgout = dbgout.replace("\n", "\n\t")
+ self.logger.debug("vtysh result:\n\t{}".format(dbgout))
+ else:
+ self.logger.debug('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.debug("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.debug("vtysh result:\n\t{}".format(dbgres))
+ else:
+ self.logger.debug('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 is 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):
+ logger = topolog.get_logger(name, log_level="debug")
+ super(TopoSwitch, self).__init__(tgen, name, **params)
+ tgen.net.add_switch(name, logger=logger)
+
+ 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')
+ * `private_mounts`: 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="{}",private_mounts="{}">'.format(
+ self.params["ip"],
+ self.params["defaultRoute"],
+ str(self.params["private_mounts"]),
+ )
+ 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 private_mounts already defined and contains functions to handle
+ ExaBGP things.
+ """
+ params["private_mounts"] = 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 is 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 ""
+
+
+class TopoBMPCollector(TopoHost):
+ PRIVATE_DIRS = [
+ "/var/log",
+ ]
+
+ def __init__(self, tgen, name, **params):
+ params["private_mounts"] = self.PRIVATE_DIRS
+ self.port = params["port"]
+ self.ip = params["ip"]
+ super(TopoBMPCollector, self).__init__(tgen, name, **params)
+
+ def __str__(self):
+ gear = super(TopoBMPCollector, self).__str__()
+ gear += " TopoBMPCollector<>".format()
+ return gear
+
+ def start(self):
+ self.run(
+ "{}/bmp_collector/bmpserver -a {} -p {}&".format(CWD, self.ip, self.port),
+ stdout=None,
+ )
+
+ def stop(self):
+ self.run("pkill -9 -f bmpserver")
+ 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",
+ "mgmtd",
+ ]:
+ 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" or fname != "mgmtd":
+ continue
+
+ os.system("{} -v 2>&1 >{}/frr_mgmtd.txt".format(path, rundir))
+ 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..901e4f6
--- /dev/null
+++ b/tests/topotests/lib/topojson.py
@@ -0,0 +1,405 @@
+# SPDX-License-Identifier: ISC
+#
+# Modified work Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Original work Copyright (c) 2018 by Network Device Education
+# Foundation, Inc. ("NetDEF")
+#
+
+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),
+ ("ospf", create_router_ospf),
+ ("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),
+ ]
+ )
+
+ 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")
+ assert False
+
+ 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)
+
+ # 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..aceb2cb
--- /dev/null
+++ b/tests/topotests/lib/topolog.py
@@ -0,0 +1,161 @@
+# SPDX-License-Identifier: ISC
+#
+# topolog.py
+# Library of helper functions for NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+Logging utilities for topology tests.
+
+This file defines our logging abstraction.
+"""
+
+import logging
+import os
+
+try:
+ from xdist import is_xdist_controller
+except ImportError:
+
+ def is_xdist_controller():
+ return False
+
+
+# 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 %(levelname)s: %(name)s: %(message)s"
+
+handlers = {}
+logger = logging.getLogger("topo")
+
+
+# Remove this and use munet version when we move to pytest_asyncio
+def get_test_logdir(nodeid=None, module=False):
+ """Get log directory relative pathname."""
+ xdist_worker = os.getenv("PYTEST_XDIST_WORKER", "")
+ mode = os.getenv("PYTEST_XDIST_MODE", "no")
+
+ # nodeid: all_protocol_startup/test_all_protocol_startup.py::test_router_running
+ # may be missing "::testname" if module is True
+ if not nodeid:
+ nodeid = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0]
+
+ cur_test = nodeid.replace("[", "_").replace("]", "_")
+ if module:
+ idx = cur_test.rfind("::")
+ path = cur_test if idx == -1 else cur_test[:idx]
+ testname = ""
+ else:
+ path, testname = cur_test.split("::")
+ testname = testname.replace("/", ".")
+ path = path[:-3].replace("/", ".")
+
+ # We use different logdir paths based on how xdist is running.
+ if mode == "each":
+ if module:
+ return os.path.join(path, "worker-logs", xdist_worker)
+ return os.path.join(path, testname, xdist_worker)
+ assert mode in ("no", "load", "loadfile", "loadscope"), f"Unknown dist mode {mode}"
+ return path if module else os.path.join(path, testname)
+
+
+def set_handler(lg, 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)
+ lg.addHandler(h)
+ return h
+
+
+def set_log_level(lg, 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)
+ lg.setLevel(log_level)
+
+
+def reset_logger(lg):
+ while lg.handlers:
+ x = lg.handlers.pop()
+ x.close()
+ lg.removeHandler(x)
+
+
+def get_logger(name, log_level=None, target=None, reset=True):
+ lg = logging.getLogger(name)
+
+ if reset:
+ reset_logger(lg)
+
+ if log_level is not None:
+ set_log_level(lg, log_level)
+
+ if target is not None:
+ set_handler(lg, target)
+
+ return lg
+
+
+def logstart(nodeid, logpath):
+ """Called from pytest before module setup."""
+ worker = os.getenv("PYTEST_TOPOTEST_WORKER", "")
+ wstr = f" on worker {worker}" if worker else ""
+ handler_id = nodeid + worker
+ logpath = logpath.absolute()
+
+ logging.debug("logstart: adding logging for %s%s at %s", nodeid, wstr, logpath)
+ root_logger = logging.getLogger()
+ handler = logging.FileHandler(logpath, mode="w")
+ handler.setFormatter(logging.Formatter(FORMAT))
+
+ root_logger.addHandler(handler)
+ handlers[handler_id] = handler
+
+ logging.debug("logstart: added logging for %s%s at %s", nodeid, wstr, logpath)
+ return handler
+
+
+def logfinish(nodeid, logpath):
+ """Called from pytest after module teardown."""
+ worker = os.getenv("PYTEST_TOPOTEST_WORKER", "")
+ wstr = f" on worker {worker}" if worker else ""
+
+ root_logger = logging.getLogger()
+
+ handler_id = nodeid + worker
+
+ if handler_id not in handlers:
+ logging.critical("can't find log handler to remove")
+ else:
+ logging.debug(
+ "logfinish: removing logging for %s%s at %s", nodeid, wstr, logpath
+ )
+ h = handlers[handler_id]
+ root_logger.removeHandler(h)
+ h.flush()
+ h.close()
+ del handlers[handler_id]
+ logging.debug(
+ "logfinish: removed logging for %s%s at %s", nodeid, wstr, logpath
+ )
+
+
+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..c220bcf
--- /dev/null
+++ b/tests/topotests/lib/topotest.py
@@ -0,0 +1,2397 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# topotest.py
+# Library of helper functions for NetDEF Topology Tests
+#
+# Copyright (c) 2016 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+import configparser
+import difflib
+import errno
+import functools
+import glob
+import json
+import os
+import platform
+import re
+import resource
+import signal
+import subprocess
+import sys
+import tempfile
+import time
+import logging
+from collections.abc import Mapping
+from copy import deepcopy
+
+import lib.topolog as topolog
+from lib.micronet_compat import Node
+from lib.topolog import logger
+from munet.base import Timeout
+
+from lib import micronet
+
+g_pytest_config = None
+
+
+def get_logs_path(rundir):
+ logspath = topolog.get_test_logdir(module=True)
+ return os.path.join(rundir, logspath)
+
+
+def gdb_core(obj, daemon, corefiles):
+ gdbcmds = r"""
+set print elements 1024
+echo -------\n
+echo threads\n
+echo -------\n
+info threads
+echo ---------\n
+echo registers\n
+echo ---------\n
+info registers
+echo ---------\n
+echo backtrace\n
+echo ---------\n
+bt
+ """
+ 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)
+ p = subprocess.run(
+ ["gdb", daemon_path, corefiles[0], "--batch"] + gdbcmds,
+ encoding="utf-8",
+ errors="ignore",
+ capture_output=True,
+ )
+ backtrace = p.stdout
+
+ #
+ # Grab the disassemble of top couple frames
+ #
+ m = re.search(r"#(\d+) .*assert.*", backtrace)
+ if not m:
+ m = re.search(r"#(\d+) .*abort.*", backtrace)
+ frames = re.findall(r"\n#(\d+) ", backtrace)
+ if m:
+ frstart = -1
+ astart = int(m.group(1)) + 1
+ ocount = f"-{int(frames[-1]) - astart + 1}"
+ else:
+ astart = -1
+ frstart = 0
+ ocount = ""
+ m = re.search(r"#(\d+) .*core_handler.*", backtrace)
+ if m:
+ frstart = int(m.group(1)) + 2
+ ocount = f"-{int(frames[-1]) - frstart + 1}"
+
+ sys.stderr.write(
+ f"\nCORE FOUND: {obj.name}: {daemon} crashed: see log for backtrace and more\n"
+ )
+
+ gdbcmds = rf"""
+set print elements 1024
+echo -------------------------\n
+echo backtrace with local args\n
+echo -------------------------\n
+bt full {ocount}
+"""
+ if frstart >= 0:
+ gdbcmds += rf"""echo ---------------------------------------\n
+echo disassemble of failing funciton (guess)\n
+echo ---------------------------------------\n
+fr {frstart}
+disassemble /m
+"""
+
+ 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)
+ p = subprocess.run(
+ ["gdb", daemon_path, corefiles[0], "-q", "--batch"] + gdbcmds,
+ encoding="utf-8",
+ errors="ignore",
+ capture_output=True,
+ )
+ btdump = p.stdout
+
+ # sys.stderr.write(
+ # "\n%s: %s crashed. Core file found - Backtrace follows:\n" % (obj.name, daemon)
+ # )
+
+ return backtrace + btdump
+
+
+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(output, expected, 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 expected == "*" or (
+ not isinstance(output, (list, dict))
+ and not isinstance(expected, (list, dict))
+ and output == expected
+ ):
+ return acc
+ elif (
+ not isinstance(output, (list, dict))
+ and not isinstance(expected, (list, dict))
+ and output != expected
+ ):
+ acc = add_error(
+ acc,
+ "output has element with value '{}' but in expected it has value '{}'".format(
+ output, expected
+ ),
+ )
+ elif (
+ isinstance(output, list)
+ and isinstance(expected, list)
+ and ((len(expected) > 0 and expected[0] == "__ordered__") or exact)
+ ):
+ if not exact:
+ del expected[0]
+ if len(output) != len(expected):
+ acc = add_error(
+ acc,
+ "output has Array of length {} but in expected it is of length {}".format(
+ len(output), len(expected)
+ ),
+ )
+ else:
+ for idx, v1, v2 in zip(range(0, len(output)), output, expected):
+ acc = merge_errors(
+ acc, gen_json_diff_report(v1, v2, exact=exact, path=add_idx(idx))
+ )
+ elif isinstance(output, list) and isinstance(expected, list):
+ if len(output) < len(expected):
+ acc = add_error(
+ acc,
+ "output has Array of length {} but in expected it is of length {}".format(
+ len(output), len(expected)
+ ),
+ )
+ else:
+ for idx2, v2 in zip(range(0, len(expected)), expected):
+ found_match = False
+ closest_diff = None
+ closest_idx = None
+ for idx1, v1 in zip(range(0, len(output)), output):
+ 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 output[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,
+ (
+ "expected has the following element at index {} which is not present in output: "
+ + "\n\n{}\n\n\tClosest match in output 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,
+ "expected has the following element at index {} which is not present in output: {}".format(
+ idx2, dump_json(v2)
+ ),
+ )
+ elif isinstance(output, dict) and isinstance(expected, dict) and exact:
+ invalid_keys_d1 = [k for k in output.keys() if k not in expected.keys()]
+ invalid_keys_d2 = [k for k in expected.keys() if k not in output.keys()]
+ for k in invalid_keys_d1:
+ acc = add_error(
+ acc, "output has key '{}' which is not present in expected".format(k)
+ )
+ for k in invalid_keys_d2:
+ acc = add_error(
+ acc, "expected has key '{}' which is not present in output".format(k)
+ )
+ valid_keys_intersection = [k for k in output.keys() if k in expected.keys()]
+ for k in valid_keys_intersection:
+ acc = merge_errors(
+ acc,
+ gen_json_diff_report(
+ output[k], expected[k], exact=exact, path=add_key(k)
+ ),
+ )
+ elif isinstance(output, dict) and isinstance(expected, dict):
+ none_keys = [k for k, v in expected.items() if v == None]
+ none_keys_present = [k for k in output.keys() if k in none_keys]
+ for k in none_keys_present:
+ acc = add_error(
+ acc, "output has key '{}' which is not supposed to be present".format(k)
+ )
+ keys = [k for k, v in expected.items() if v != None]
+ invalid_keys_intersection = [k for k in keys if k not in output.keys()]
+ for k in invalid_keys_intersection:
+ acc = add_error(
+ acc, "expected has key '{}' which is not present in output".format(k)
+ )
+ valid_keys_intersection = [k for k in keys if k in output.keys()]
+ for k in valid_keys_intersection:
+ acc = merge_errors(
+ acc,
+ gen_json_diff_report(
+ output[k], expected[k], exact=exact, path=add_key(k)
+ ),
+ )
+ else:
+ acc = add_error(
+ acc,
+ "output has element of type '{}' but the corresponding element in expected is of type '{}'".format(
+ json_type(output), json_type(expected)
+ ),
+ points=2,
+ )
+
+ return acc
+
+
+def json_cmp(output, expected, exact=False):
+ """
+ JSON compare function. Receives two parameters:
+ * `output`: parsed JSON data structure from outputed vtysh command
+ * `expected``: parsed JSON data structure from what is expected to be seen
+
+ Returns 'None' when all JSON Object keys and all Array elements of expected have a match
+ in output, i.e., when expected is a "subset" of output 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 output and expected 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 output
+ * using '*' as JSON Object value or Array value is checking for presence in output
+ without checking the values
+ * using '__ordered__' as first element in a JSON Array in expected will also check the
+ order when it is compared to an Array in output
+ """
+
+ (errors_n, errors) = gen_json_diff_report(
+ deepcopy(output), deepcopy(expected), 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__
+
+ # Just a safety-check to avoid running topotests with very
+ # small wait/count arguments.
+ wait_time = wait * count
+ if wait_time < 5:
+ assert (
+ wait_time >= 5
+ ), "Waiting time is too small (count={}, wait={}), adjust timer values".format(
+ count, wait
+ )
+
+ logger.debug(
+ "'{}' 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.debug(
+ "'{}' 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__
+
+ # Just a safety-check to avoid running topotests with very
+ # small wait/count arguments.
+ wait_time = wait * count
+ if wait_time < 5:
+ assert (
+ wait_time >= 5
+ ), "Waiting time is too small (count={}, wait={}), adjust timer values".format(
+ count, wait
+ )
+
+ logger.debug(
+ "'{}' 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.debug(
+ "'{}' 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 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 iproute2_is_fdb_get_capable():
+ """
+ Checks if the iproute2 version installed on the system is capable of
+ handling `bridge fdb get` commands to query neigh table resolution.
+
+ Returns True if capability can be detected, returns False otherwise.
+ """
+
+ if is_linux():
+ try:
+ subp = subprocess.Popen(
+ ["bridge", "fdb", "get", "help"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ )
+ iproute2_out = subp.communicate()[1].splitlines()[0].split()[0]
+
+ if "Usage" in str(iproute2_out):
+ 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 = list(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 + ".asan." + component + ".*"
+ 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.debug("Increasing sysctl %s from %s to %s", variable, cur_val, valstr)
+ commander.cmd_raises('sysctl -w {}="{}"'.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.debug("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:
+ logger = logging.getLogger("topotest")
+ commander = micronet.Commander("sysctl", logger=logger)
+
+ 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:
+ logger = logging.getLogger("topotest")
+ commander = micronet.Commander("sysctl", logger=logger)
+ 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.debug("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.* {0}/{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, *posargs, **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")
+ )
+
+ self.perf_daemons = {}
+
+ # 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_pytest_config.getoption("--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, *posargs, **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,
+ "mgmtd": 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))
+
+ mgmtd_path = os.path.join(self.daemondir, "mgmtd")
+ if not os.path.isfile(mgmtd_path):
+ raise Exception("FRR MGMTD binary doesn't exist at {}".format(mgmtd_path))
+
+ # pylint: disable=W0221
+ # Some params are only meaningful for the parent class.
+ def config_host(self, **params):
+ super(Router, self).config_host(**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))
+
+ cpath = os.path.join(self.daemondir, "mgmtd")
+ if not os.path.isfile(zpath):
+ raise Exception("No MGMTD binary found in {}".format(cpath))
+ # 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.debug("{}: sending SIGTERM to {}".format(self.name, name))
+ try:
+ os.kill(pid, signal.SIGTERM)
+ except OSError as err:
+ logger.debug(
+ "%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 running:
+ 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 -4 address flush " + interface)
+ self.intf_ip_cmd(
+ interface, "ip -6 address flush " + interface + " scope global"
+ )
+ except Exception as ex:
+ logger.error("%s can't remove IPs %s", self, str(ex))
+ # breakpoint()
+ # 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.
+ source_was_none = source is None
+ if source_was_none:
+ source = f"{daemon}.conf"
+
+ # "" to avoid loading a default config which is present in router dir
+ 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.debug(
+ "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 and not os.path.exists(source):
+ logger.warning(
+ "missing config '%s' for '%s' creating empty file '%s'",
+ self.name,
+ source,
+ conf_file,
+ )
+ if daemon == "frr" or not self.unified_config:
+ self.cmd_raises("rm -f " + conf_file)
+ self.cmd_raises("touch " + conf_file)
+ self.cmd_raises(
+ "chown {0}:{0} {1}".format(self.routertype, conf_file)
+ )
+ self.cmd_raises("chmod 664 {}".format(conf_file))
+ elif source:
+ # copy zebra.conf to mgmtd folder, which can be used during startup
+ if daemon == "zebra" and not self.unified_config:
+ conf_file_mgmt = "/etc/{}/{}.conf".format(self.routertype, "mgmtd")
+ logger.debug(
+ "copying '%s' as '%s' on '%s'",
+ source,
+ conf_file_mgmt,
+ self.name,
+ )
+ self.cmd_raises("cp {} {}".format(source, conf_file_mgmt))
+ self.cmd_raises(
+ "chown {0}:{0} {1}".format(self.routertype, conf_file_mgmt)
+ )
+ self.cmd_raises("chmod 664 {}".format(conf_file_mgmt))
+
+ logger.debug(
+ "copying '%s' as '%s' on '%s'", source, conf_file, self.name
+ )
+ self.cmd_raises("cp {} {}".format(source, conf_file))
+ 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["mgmtd"] == 0):
+ # Add mgmtd with zebra - if it exists
+ mgmtd_path = os.path.join(self.daemondir, "mgmtd")
+ if os.path.isfile(mgmtd_path):
+ self.daemons["mgmtd"] = 1
+ self.daemons_options["mgmtd"] = ""
+ # Auto-Started mgmtd has no config, so it will read from zebra config
+
+ if (daemon == "zebra") and (self.daemons["staticd"] == 0):
+ # Add staticd with zebra - if it exists
+ staticd_path = os.path.join(self.daemondir, "staticd")
+ 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.warning("No daemon {} known".format(daemon))
+
+ return source if os.path.exists(source) else ""
+
+ 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 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")
+
+ if g_pytest_config.name_in_option_list(self.name, "--shell"):
+ 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)
+
+ if g_pytest_config.name_in_option_list(self.name, "--vtysh"):
+ 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):
+ filename = "{}/{}/{}.{}".format(self.logdir, self.name, daemon, log)
+ log = ""
+ with open(filename) as file:
+ log = file.read()
+ return log
+
+ def startRouterDaemons(self, daemons=None, tgen=None):
+ "Starts FRR daemons for this router."
+
+ asan_abort = bool(g_pytest_config.option.asan_abort)
+ gdb_breakpoints = g_pytest_config.get_option_list("--gdb-breakpoints")
+ gdb_daemons = g_pytest_config.get_option_list("--gdb-daemons")
+ gdb_routers = g_pytest_config.get_option_list("--gdb-routers")
+ valgrind_extra = bool(g_pytest_config.option.valgrind_extra)
+ valgrind_memleaks = bool(g_pytest_config.option.valgrind_memleaks)
+ strace_daemons = g_pytest_config.get_option_list("--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 is None:
+ self.version = self.cmd(
+ os.path.join(self.daemondir, "bgpd") + " -v"
+ ).split()[2]
+ logger.info("{}: running version: {}".format(self.name, self.version))
+
+ perfds = {}
+ perf_options = g_pytest_config.get_option("--perf-options", "-g")
+ for perf in g_pytest_config.get_option("--perf", []):
+ if "," in perf:
+ daemon, routers = perf.split(",", 1)
+ perfds[daemon] = routers.split(",")
+ else:
+ daemon = perf
+ perfds[daemon] = ["all"]
+
+ logd_options = {}
+ for logd in g_pytest_config.get_option("--logd", []):
+ if "," in logd:
+ daemon, routers = logd.split(",", 1)
+ logd_options[daemon] = routers.split(",")
+ else:
+ daemon = logd
+ logd_options[daemon] = ["all"]
+
+ # 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)
+
+ tail_log_files = []
+ check_daemon_files = []
+
+ def start_daemon(daemon, extra_opts=None):
+ daemon_opts = self.daemons_options.get(daemon, "")
+
+ # get pid and vty filenames and remove the files
+ m = re.match(r"(.* |^)-n (\d+)( ?.*|$)", daemon_opts)
+ dfname = daemon if not m else "{}-{}".format(daemon, m.group(2))
+ runbase = "/var/run/{}/{}".format(self.routertype, dfname)
+ # If this is a new system bring-up remove the pid/vty files, otherwise
+ # do not since apparently presence of the pidfile impacts BGP GR
+ self.cmd_status("rm -f {0}.pid {0}.vty".format(runbase))
+
+ 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
+ ) + "{}.pid -x /etc/frr/agentx".format(runbase)
+ # check_daemon_files.append(runbase + ".pid")
+ else:
+ binary = os.path.join(self.daemondir, daemon)
+ check_daemon_files.extend([runbase + ".pid", runbase + ".vty"])
+
+ cmdenv = "ASAN_OPTIONS="
+ if asan_abort:
+ cmdenv += "abort_on_error=1:"
+ cmdenv += "log_path={0}/{1}.asan.{2} ".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 ".format(daemon_opts)
+ cmdopt += "--log file:{}.log --log-level debug".format(daemon)
+
+ if daemon in logd_options:
+ logdopt = logd_options[daemon]
+ if "all" in logdopt or self.name in logdopt:
+ tail_log_files.append(
+ "{}/{}/{}.log".format(self.logdir, self.name, 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
+ )
+ elif daemon in perfds and (
+ self.name in perfds[daemon] or "all" in perfds[daemon]
+ ):
+ cmdopt += rediropt
+ cmd = " ".join(
+ ["perf record {} --".format(perf_options), binary, cmdopt]
+ )
+ p = self.popen(cmd)
+ self.perf_daemons[daemon] = p
+ if p.poll() and p.returncode:
+ self.logger.error(
+ '%s: Failed to launch "%s" (%s) with perf using: %s',
+ self,
+ daemon,
+ p.returncode,
+ cmd,
+ )
+ else:
+ logger.debug(
+ "%s: %s %s started with perf", 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.debug("%s: %s %s started", self, self.routertype, daemon)
+
+ # Start mgmtd first
+ if "mgmtd" in daemons_list:
+ start_daemon("mgmtd")
+ while "mgmtd" in daemons_list:
+ daemons_list.remove("mgmtd")
+
+ # Start Zebra after mgmtd
+ 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")
+
+ # 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.
+ wait_time = 30 if (gdb_routers or gdb_daemons) else 10
+ timeout = Timeout(wait_time)
+ for remaining in timeout:
+ if not check_daemon_files:
+ break
+ check = check_daemon_files[0]
+ if self.path_exists(check):
+ check_daemon_files.pop(0)
+ continue
+ self.logger.debug("Waiting {}s for {} to appear".format(remaining, check))
+ time.sleep(0.5)
+
+ if check_daemon_files:
+ assert False, "Timeout({}) waiting for {} to appear on {}".format(
+ wait_time, check_daemon_files[0], self.name
+ )
+
+ # 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))
+
+ if "frr" in logd_options:
+ logdopt = logd_options["frr"]
+ if "all" in logdopt or self.name in logdopt:
+ tail_log_files.append("{}/{}/frr.log".format(self.logdir, self.name))
+
+ for tailf in tail_log_files:
+ self.run_in_window("tail -n10000 -F " + tailf, title=tailf, background=True)
+
+ return ""
+
+ def pid_exists(self, pid):
+ if pid <= 0:
+ return False
+ try:
+ # If we are not using PID namespaces then we will be a parent of the pid,
+ # otherwise the init process of the PID namespace will have reaped the proc.
+ os.waitpid(pid, os.WNOHANG)
+ except Exception:
+ pass
+
+ rc, o, e = self.cmd_status("kill -0 " + str(pid), warn=False)
+ return rc == 0 or "No such process" not in e
+
+ 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):
+ daemonpidfile = d.rstrip()
+ daemonpid = self.cmd("cat %s" % daemonpidfile).rstrip()
+ if daemonpid.isdigit() and self.pid_exists(int(daemonpid)):
+ logger.debug(
+ "{}: killing {}".format(
+ self.name,
+ os.path.basename(daemonpidfile.rsplit(".", 1)[0]),
+ )
+ )
+ self.cmd_status("kill -KILL {}".format(daemonpid))
+ if self.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 self.pid_exists(
+ int(daemonpid)
+ ):
+ logger.info(
+ "{}: killing {}".format(
+ self.name,
+ os.path.basename(
+ d.rstrip().rsplit(".", 1)[0]
+ ),
+ )
+ )
+ self.cmd_status(
+ "kill -KILL {}".format(daemonpid)
+ )
+ if daemonpid.isdigit() and not self.pid_exists(
+ int(daemonpid)
+ ):
+ numRunning -= 1
+ self.cmd("rm -- {}".format(daemonpidfile))
+ 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
+ + f"\nCORE FOUND: {self.name}: {daemon} crashed. Backtrace follows:\n{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.warning(
+ "\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/mgmt_config/r1/early-end-zebra.conf b/tests/topotests/mgmt_config/r1/early-end-zebra.conf
new file mode 100644
index 0000000..44a2f96
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/early-end-zebra.conf
@@ -0,0 +1,6 @@
+allow-external-route-update
+end
+ip multicast rpf-lookup-mode urib-only
+end
+ip table range 2 3
+end \ No newline at end of file
diff --git a/tests/topotests/mgmt_config/r1/early-end.conf b/tests/topotests/mgmt_config/r1/early-end.conf
new file mode 100644
index 0000000..3aacad6
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/early-end.conf
@@ -0,0 +1,8 @@
+ip route 15.1.0.0/24 101.0.0.2
+end
+ip route 15.2.0.0/24 101.0.0.2
+end
+ip route 15.3.0.0/24 101.0.0.2
+end
+ip route 15.4.0.0/24 101.0.0.2
+end \ No newline at end of file
diff --git a/tests/topotests/mgmt_config/r1/early-end2-zebra.conf b/tests/topotests/mgmt_config/r1/early-end2-zebra.conf
new file mode 100644
index 0000000..37619d5
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/early-end2-zebra.conf
@@ -0,0 +1,7 @@
+conf t
+allow-external-route-update
+end
+ip multicast rpf-lookup-mode urib-only
+end
+ip table range 2 3
+end \ No newline at end of file
diff --git a/tests/topotests/mgmt_config/r1/early-end2.conf b/tests/topotests/mgmt_config/r1/early-end2.conf
new file mode 100644
index 0000000..229ccc7
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/early-end2.conf
@@ -0,0 +1,9 @@
+conf t
+ip route 16.1.0.0/24 101.0.0.2
+end
+ip route 16.2.0.0/24 101.0.0.2
+end
+ip route 16.3.0.0/24 101.0.0.2
+end
+ip route 16.4.0.0/24 101.0.0.2
+end \ No newline at end of file
diff --git a/tests/topotests/mgmt_config/r1/early-exit-zebra.conf b/tests/topotests/mgmt_config/r1/early-exit-zebra.conf
new file mode 100644
index 0000000..44f202d
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/early-exit-zebra.conf
@@ -0,0 +1,6 @@
+allow-external-route-update
+exit
+ip multicast rpf-lookup-mode urib-only
+exit
+ip table range 2 3
+exit \ No newline at end of file
diff --git a/tests/topotests/mgmt_config/r1/early-exit.conf b/tests/topotests/mgmt_config/r1/early-exit.conf
new file mode 100644
index 0000000..c6a52df
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/early-exit.conf
@@ -0,0 +1,8 @@
+ip route 13.1.0.0/24 101.0.0.2
+exit
+ip route 13.2.0.0/24 101.0.0.2
+exit
+ip route 13.3.0.0/24 101.0.0.2
+exit
+ip route 13.4.0.0/24 101.0.0.2
+exit \ No newline at end of file
diff --git a/tests/topotests/mgmt_config/r1/early-exit2-zebra.conf b/tests/topotests/mgmt_config/r1/early-exit2-zebra.conf
new file mode 100644
index 0000000..c7109bf
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/early-exit2-zebra.conf
@@ -0,0 +1,7 @@
+conf t
+allow-external-route-update
+exit
+ip multicast rpf-lookup-mode urib-only
+exit
+ip table range 2 3
+exit \ No newline at end of file
diff --git a/tests/topotests/mgmt_config/r1/early-exit2.conf b/tests/topotests/mgmt_config/r1/early-exit2.conf
new file mode 100644
index 0000000..79510c0
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/early-exit2.conf
@@ -0,0 +1,9 @@
+conf t
+ip route 14.1.0.0/24 101.0.0.2
+exit
+ip route 14.2.0.0/24 101.0.0.2
+exit
+ip route 14.3.0.0/24 101.0.0.2
+exit
+ip route 14.4.0.0/24 101.0.0.2
+exit \ No newline at end of file
diff --git a/tests/topotests/mgmt_config/r1/frr.conf b/tests/topotests/mgmt_config/r1/frr.conf
new file mode 100644
index 0000000..076715c
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/frr.conf
@@ -0,0 +1,15 @@
+debug northbound notifications
+! debug northbound libyang
+debug northbound events
+debug northbound callbacks
+debug mgmt backend datastore frontend transaction
+debug mgmt client backend
+debug mgmt client frontend
+
+log timestamp precision 6
+log file frr.log debug
+
+interface r1-eth0
+ ip address 101.0.0.1/24
+ ipv6 address 2101::1/64
+exit \ No newline at end of file
diff --git a/tests/topotests/mgmt_config/r1/mgmtd.conf b/tests/topotests/mgmt_config/r1/mgmtd.conf
new file mode 100644
index 0000000..318de76
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/mgmtd.conf
@@ -0,0 +1,11 @@
+debug northbound notifications
+debug northbound libyang
+debug northbound events
+debug northbound callbacks
+debug mgmt backend datastore frontend transaction
+debug mgmt client backend
+debug mgmt client frontend
+
+ip route 12.0.0.0/24 101.0.0.2
+
+ipv6 route 2012::/48 2101::2 \ No newline at end of file
diff --git a/tests/topotests/mgmt_config/r1/normal-exit.conf b/tests/topotests/mgmt_config/r1/normal-exit.conf
new file mode 100644
index 0000000..c6a52df
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/normal-exit.conf
@@ -0,0 +1,8 @@
+ip route 13.1.0.0/24 101.0.0.2
+exit
+ip route 13.2.0.0/24 101.0.0.2
+exit
+ip route 13.3.0.0/24 101.0.0.2
+exit
+ip route 13.4.0.0/24 101.0.0.2
+exit \ No newline at end of file
diff --git a/tests/topotests/mgmt_config/r1/one-exit-zebra.conf b/tests/topotests/mgmt_config/r1/one-exit-zebra.conf
new file mode 100644
index 0000000..0c38459
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/one-exit-zebra.conf
@@ -0,0 +1,3 @@
+allow-external-route-update
+exit
+ip multicast rpf-lookup-mode urib-only
diff --git a/tests/topotests/mgmt_config/r1/one-exit.conf b/tests/topotests/mgmt_config/r1/one-exit.conf
new file mode 100644
index 0000000..47147d4
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/one-exit.conf
@@ -0,0 +1,3 @@
+ip route 20.1.0.0/24 101.0.0.2
+exit
+ip route 20.2.0.0/24 101.0.0.2
diff --git a/tests/topotests/mgmt_config/r1/one-exit2-zebra.conf b/tests/topotests/mgmt_config/r1/one-exit2-zebra.conf
new file mode 100644
index 0000000..34acb76
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/one-exit2-zebra.conf
@@ -0,0 +1,4 @@
+conf t
+allow-external-route-update
+exit
+ip multicast rpf-lookup-mode urib-only \ No newline at end of file
diff --git a/tests/topotests/mgmt_config/r1/one-exit2.conf b/tests/topotests/mgmt_config/r1/one-exit2.conf
new file mode 100644
index 0000000..262339a
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/one-exit2.conf
@@ -0,0 +1,4 @@
+conf t
+ip route 21.1.0.0/24 101.0.0.2
+exit
+ip route 21.2.0.0/24 101.0.0.2
diff --git a/tests/topotests/mgmt_config/r1/zebra.conf b/tests/topotests/mgmt_config/r1/zebra.conf
new file mode 100644
index 0000000..f3264ef
--- /dev/null
+++ b/tests/topotests/mgmt_config/r1/zebra.conf
@@ -0,0 +1,7 @@
+log timestamp precision 6
+log file frr-r1.log debug
+
+interface r1-eth0
+ ip address 101.0.0.1/24
+ ipv6 address 2101::1/64
+exit \ No newline at end of file
diff --git a/tests/topotests/mgmt_config/test_config.py b/tests/topotests/mgmt_config/test_config.py
new file mode 100644
index 0000000..b07ed8f
--- /dev/null
+++ b/tests/topotests/mgmt_config/test_config.py
@@ -0,0 +1,385 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+#
+# June 10 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+"""
+Test mgmtd parsing of configs.
+
+So:
+
+MGMTD matches zebra:
+
+one exit file: ONE: vty -f file
+one exit redir: ONE: vty < file
+early exit file: ONE: vty -f file
+early exit redir: ONE: vty < file
+early end file: ALL: vty -f file
+early end redir: ONE: vty < file
+
+Raw tests:
+
+FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_file - AssertionError: vtysh < didn't work after exit
+FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_redir - AssertionError: vtysh < didn't work after exit
+FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_file - AssertionError: vtysh -f didn't work after 1 exit
+FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_redir - AssertionError: vtysh < didn't work after 1 exits
+FAILED mgmt_config/test_config.py::test_mgmtd_early_end_redir - AssertionError: vtysh < didn't work after 1 end
+
+FAILED mgmt_config/test_config.py::test_zebra_one_exit_file - AssertionError: zebra second conf missing
+FAILED mgmt_config/test_config.py::test_zebra_one_exit_redir - AssertionError: zebra second conf missing
+FAILED mgmt_config/test_config.py::test_zebra_early_exit_file - AssertionError: zebra second conf missing
+FAILED mgmt_config/test_config.py::test_zebra_early_exit_redir - AssertionError: zebra second conf missing
+FAILED mgmt_config/test_config.py::test_zebra_early_end_redir - AssertionError: zebra second conf missing
+
+Before fixed:
+
+one exit file: NONE: vty -f file
+early exit file: NONE: vty -f file
+
+FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_file - AssertionError: vtysh -f didn't work before exit
+FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_redir - AssertionError: vtysh < didn't work after exit
+FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_file - AssertionError: vtysh -f didn't work before exit
+FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_redir - AssertionError: vtysh < didn't work after 1 exits
+FAILED mgmt_config/test_config.py::test_mgmtd_early_end_redir - AssertionError: vtysh < didn't work after 1 end
+
+FAILED mgmt_config/test_config.py::test_zebra_one_exit_file - AssertionError: zebra second conf missing
+FAILED mgmt_config/test_config.py::test_zebra_one_exit_redir - AssertionError: zebra second conf missing
+FAILED mgmt_config/test_config.py::test_zebra_early_exit_file - AssertionError: zebra second conf missing
+FAILED mgmt_config/test_config.py::test_zebra_early_exit_redir - AssertionError: zebra second conf missing
+FAILED mgmt_config/test_config.py::test_zebra_early_end_redir - AssertionError: zebra second conf missing
+
+"""
+import ipaddress
+import logging
+import os
+import re
+from pathlib import Path
+
+import pytest
+from lib.common_config import retry, step
+from lib.topogen import Topogen, TopoRouter
+
+# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd]
+pytestmark = [pytest.mark.staticd]
+
+
+@retry(retry_timeout=1, initial_wait=0.1)
+def check_kernel(r1, prefix, expected=True):
+ net = ipaddress.ip_network(prefix)
+ if net.version == 6:
+ kernel = r1.cmd_nostatus("ip -6 route show", warn=not expected)
+ else:
+ kernel = r1.cmd_nostatus("ip -4 route show", warn=not expected)
+
+ logging.debug("checking kernel routing table:\n%0.1920s", kernel)
+ route = f"{str(net)}(?: nhid [0-9]+)?.*proto (static|196)"
+ m = re.search(route, kernel)
+ if expected and not m:
+ return f"Failed to find \n'{route}'\n in \n'{kernel:.1920}'"
+ elif not expected and m:
+ return f"Failed found \n'{route}'\n in \n'{kernel:.1920}'"
+ return None
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ topodef = {"s1": ("r1",)}
+
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ # configure mgmtd using current mgmtd config file
+ tgen.gears["r1"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ tgen.gears["r1"].load_config(TopoRouter.RD_MGMTD)
+
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+def save_log_snippet(logfile, content, savepath=None):
+ os.sync()
+ os.sync()
+ os.sync()
+
+ with open(logfile, encoding="utf-8") as f:
+ buf = f.read()
+ assert content == buf[: len(content)]
+ newcontent = buf[len(content) :]
+
+ if savepath:
+ with open(savepath, "w", encoding="utf-8") as f:
+ f.write(newcontent)
+
+ return buf
+
+
+def mapname(lname):
+ return lname.replace(".conf", "") + "-log.txt"
+
+
+logbuf = ""
+
+
+@pytest.fixture(scope="module")
+def r1(tgen):
+ return tgen.gears["r1"].net
+
+
+@pytest.fixture(scope="module")
+def confdir():
+ return Path(os.environ["PYTEST_TOPOTEST_SCRIPTDIR"]) / "r1"
+
+
+@pytest.fixture(scope="module")
+def tempdir(r1):
+ return Path(r1.rundir)
+
+
+@pytest.fixture(scope="module")
+def logpath(tempdir):
+ return tempdir / "mgmtd.log"
+
+
+@pytest.fixture(autouse=True, scope="function")
+def cleanup_config(r1, tempdir, logpath):
+ global logbuf
+
+ logbuf = save_log_snippet(logpath, logbuf, "/dev/null")
+
+ yield
+
+ r1.cmd_nostatus("vtysh -c 'conf t' -c 'no allow-external-route-update'")
+ r1.cmd_nostatus("vtysh -c 'conf t' -c 'no ip multicast rpf-lookup-mode urib-only'")
+ r1.cmd_nostatus("vtysh -c 'conf t' -c 'no ip table range 2 3'")
+
+ logbuf = save_log_snippet(logpath, logbuf, "/dev/null")
+
+
+def test_staticd_startup(r1):
+ r1.cmd_nostatus(
+ "vtysh -c 'debug mgmt client frontend' "
+ "-c 'debug mgmt client backend' "
+ "-c 'debug mgmt backend frontend datastore transaction'"
+ )
+ step("Verifying routes are present on r1")
+ result = check_kernel(r1, "12.0.0.0/24", retry_timeout=3.0)
+ assert result is None
+
+
+def test_mgmtd_one_exit_file(r1, confdir, tempdir, logpath):
+ global logbuf
+
+ conf = "one-exit.conf"
+ step(f"load {conf} file with vtysh -f ")
+ output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}")
+ logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
+ print(output)
+
+ result1 = check_kernel(r1, "20.1.0.0/24")
+ result2 = check_kernel(r1, "20.2.0.0/24")
+
+ assert result1 is None, "vtysh -f didn't work before exit"
+ assert result2 is not None, "vtysh < worked after exit, unexpected"
+
+
+def test_mgmtd_one_exit_redir(r1, confdir, tempdir, logpath):
+ global logbuf
+
+ conf = "one-exit2.conf"
+ step(f"Redirect {conf} file into vtysh")
+ output = r1.cmd_nostatus(f"vtysh < {confdir / conf}")
+ logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
+ print(output)
+
+ result1 = check_kernel(r1, "21.1.0.0/24")
+ result2 = check_kernel(r1, "21.2.0.0/24")
+
+ assert result1 is None, "vtysh < didn't work before exit"
+ assert result2 is not None, "vtysh < worked after exit, unexpected"
+
+
+def test_mgmtd_early_exit_file(r1, confdir, tempdir, logpath):
+ global logbuf
+
+ conf = "early-exit.conf"
+ step(f"load {conf} file with vtysh -f ")
+ output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}")
+ logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
+ print(output)
+
+ result1 = check_kernel(r1, "13.1.0.0/24")
+ result2 = check_kernel(r1, "13.2.0.0/24")
+ result3 = check_kernel(r1, "13.3.0.0/24")
+
+ assert result1 is None, "vtysh -f didn't work before exit"
+ assert result2 is not None, "vtysh -f worked after 1 exit, unexpected"
+ assert result3 is not None, "vtysh -f worked after 2 exit, unexpected"
+
+
+def test_mgmtd_early_exit_redir(r1, confdir, tempdir, logpath):
+ global logbuf
+
+ conf = "early-exit2.conf"
+ step(f"Redirect {conf} file into vtysh")
+ output = r1.cmd_nostatus(f"vtysh < {confdir / conf}")
+ logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
+ print(output)
+
+ result1 = check_kernel(r1, "14.1.0.0/24")
+ result2 = check_kernel(r1, "14.2.0.0/24")
+ result3 = check_kernel(r1, "14.3.0.0/24")
+
+ assert result1 is None, "vtysh < didn't work before exit"
+ assert result2 is not None, "vtysh < worked after 1 exits, unexpected"
+ assert result3 is not None, "vtysh < worked after 2 exits, unexpected"
+
+
+def test_mgmtd_early_end_file(r1, confdir, tempdir, logpath):
+ global logbuf
+
+ conf = "early-end.conf"
+ step(f"load {conf} file with vtysh -f ")
+ output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}")
+ logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
+ print(output)
+
+ result1 = check_kernel(r1, "15.1.0.0/24")
+ result2 = check_kernel(r1, "15.2.0.0/24")
+ result3 = check_kernel(r1, "15.3.0.0/24")
+
+ assert result1 is None, "vtysh -f didn't work before end"
+ assert result2 is None, "vtysh -f didn't work after 1 end"
+ assert result3 is None, "vtysh -f didn't work after 2 ends"
+
+
+def test_mgmtd_early_end_redir(r1, confdir, tempdir, logpath):
+ global logbuf
+
+ conf = "early-end2.conf"
+ step(f"Redirect {conf} file into vtysh")
+ output = r1.cmd_nostatus(f"vtysh < {confdir / conf}")
+ logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
+ print(output)
+
+ result1 = check_kernel(r1, "16.1.0.0/24")
+ result2 = check_kernel(r1, "16.2.0.0/24")
+ result3 = check_kernel(r1, "16.3.0.0/24")
+
+ assert result1 is None, "vtysh < didn't work before end"
+ assert result2 is not None, "vtysh < worked after 1 end, unexpected"
+ assert result3 is not None, "vtysh < worked after 2 end, unexpected"
+
+
+#
+# Zebra
+#
+
+
+def test_zebra_one_exit_file(r1, confdir, tempdir, logpath):
+ global logbuf
+
+ conf = "one-exit-zebra.conf"
+ step(f"load {conf} file with vtysh -f ")
+ output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}")
+ logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
+ print(output)
+
+ showrun = r1.cmd_nostatus("vtysh -c 'show running'")
+ assert "allow-external-route-update" in showrun, "zebra conf missing"
+ assert (
+ "ip multicast rpf-lookup-mode urib-only" not in showrun
+ ), "zebra second conf present, unexpected"
+
+
+def test_zebra_one_exit_redir(r1, confdir, tempdir, logpath):
+ global logbuf
+
+ conf = "one-exit2-zebra.conf"
+ step(f"Redirect {conf} file into vtysh")
+ output = r1.cmd_nostatus(f"vtysh < {confdir / conf}")
+ logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
+ print(output)
+
+ showrun = r1.cmd_nostatus("vtysh -c 'show running'")
+
+ assert "allow-external-route-update" in showrun, "zebra conf missing"
+ assert (
+ "ip multicast rpf-lookup-mode urib-only" not in showrun
+ ), "zebra second conf present, unexpected"
+
+
+def test_zebra_early_exit_file(r1, confdir, tempdir, logpath):
+ global logbuf
+
+ conf = "early-exit-zebra.conf"
+ step(f"load {conf} file with vtysh -f ")
+ output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}")
+ logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
+ print(output)
+
+ showrun = r1.cmd_nostatus("vtysh -c 'show running'")
+
+ assert "allow-external-route-update" in showrun, "zebra conf missing"
+ assert (
+ "ip multicast rpf-lookup-mode urib-only" not in showrun
+ ), "zebra second conf present, unexpected"
+ assert "ip table range 2 3" not in showrun, "zebra third conf present, unexpected"
+
+
+def test_zebra_early_exit_redir(r1, confdir, tempdir, logpath):
+ global logbuf
+
+ conf = "early-exit2-zebra.conf"
+ step(f"Redirect {conf} file into vtysh")
+ output = r1.cmd_nostatus(f"vtysh < {confdir / conf}")
+ logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
+ print(output)
+
+ showrun = r1.cmd_nostatus("vtysh -c 'show running'")
+
+ assert "allow-external-route-update" in showrun, "zebra conf missing"
+ assert (
+ "ip multicast rpf-lookup-mode urib-only" not in showrun
+ ), "zebra second conf present, unexpected"
+ assert "ip table range 2 3" not in showrun, "zebra third conf present, unexpected"
+
+
+def test_zebra_early_end_file(r1, confdir, tempdir, logpath):
+ global logbuf
+
+ conf = "early-end-zebra.conf"
+ step(f"load {conf} file with vtysh -f ")
+ output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}")
+ logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
+ print(output)
+
+ showrun = r1.cmd_nostatus("vtysh -c 'show running'")
+
+ assert "allow-external-route-update" in showrun, "zebra conf missing"
+ assert (
+ "ip multicast rpf-lookup-mode urib-only" in showrun
+ ), "zebra second conf missing"
+ assert "ip table range 2 3" in showrun, "zebra third missing"
+
+
+def test_zebra_early_end_redir(r1, confdir, tempdir, logpath):
+ global logbuf
+
+ conf = "early-end2-zebra.conf"
+ step(f"Redirect {conf} file into vtysh")
+ output = r1.cmd_nostatus(f"vtysh < {confdir / conf}")
+ logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
+ print(output)
+
+ showrun = r1.cmd_nostatus("vtysh -c 'show running'")
+
+ assert "allow-external-route-update" in showrun, "zebra conf missing"
+ assert (
+ "ip multicast rpf-lookup-mode urib-only" not in showrun
+ ), "zebra second conf present, unexpected"
+ assert "ip table range 2 3" not in showrun, "zebra third conf present, unexpected"
diff --git a/tests/topotests/mgmt_config/test_regression.py b/tests/topotests/mgmt_config/test_regression.py
new file mode 100644
index 0000000..00c3e01
--- /dev/null
+++ b/tests/topotests/mgmt_config/test_regression.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+#
+# July 13 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+"""
+Test mgmtd regressions
+
+"""
+import pytest
+from lib.topogen import Topogen
+
+pytestmark = [pytest.mark.staticd]
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ topodef = {"s1": ("r1",)}
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+ tgen.gears["r1"].load_frr_config("frr.conf")
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+def test_regression_issue_13920(tgen):
+ """Issue #13920
+
+ ubuntu2204# conf t
+ ubuntu2204(config)# ip route 3.2.4.0/24 6.5.5.11 loop3
+ ubuntu2204(config)# nexthop-group nh2
+ ubuntu2204(config-nh-group)# nexthop 6.5.5.12
+ ubuntu2204(config-nh-group)# exi
+ ubuntu2204(config)# ip route 3.22.4.0/24 6.5.5.12
+ crash
+ """
+
+ r1 = tgen.gears["r1"]
+ r1.vtysh_multicmd(
+ """
+ conf t
+ nexthop-group nh2
+ exit
+ ip route 3.22.4.0/24 6.5.5.12
+ """
+ )
+ output = r1.net.checkRouterCores()
+ assert not output.strip()
diff --git a/tests/topotests/mgmt_startup/r1/mgmtd.conf b/tests/topotests/mgmt_startup/r1/mgmtd.conf
new file mode 100644
index 0000000..ecc829c
--- /dev/null
+++ b/tests/topotests/mgmt_startup/r1/mgmtd.conf
@@ -0,0 +1,13 @@
+debug northbound notifications
+debug northbound libyang
+debug northbound events
+debug northbound callbacks
+debug mgmt backend datastore frontend transaction
+debug mgmt client backend
+debug mgmt client frontend
+
+ip route 12.0.0.0/24 101.0.0.2
+ip route 13.0.0.0/24 101.0.0.3
+
+ipv6 route 2012::/48 2101::2
+ipv6 route 2013::/48 2101::3
diff --git a/tests/topotests/mgmt_startup/r1/zebra.conf b/tests/topotests/mgmt_startup/r1/zebra.conf
new file mode 100644
index 0000000..98cc95b
--- /dev/null
+++ b/tests/topotests/mgmt_startup/r1/zebra.conf
@@ -0,0 +1,7 @@
+log timestamp precision 3
+log file frr2.log
+
+interface r1-eth0
+ ip address 101.0.0.1/24
+ ipv6 address 2101::1/64
+exit \ No newline at end of file
diff --git a/tests/topotests/mgmt_startup/r2/staticd.conf b/tests/topotests/mgmt_startup/r2/staticd.conf
new file mode 100644
index 0000000..b581ed2
--- /dev/null
+++ b/tests/topotests/mgmt_startup/r2/staticd.conf
@@ -0,0 +1,7 @@
+log timestamp precision 3
+
+ip route 11.0.0.0/24 101.0.0.1
+ip route 13.0.0.0/24 101.0.0.3
+
+ipv6 route 2011::/48 2102::1
+ipv6 route 2013::/48 2102::3 \ No newline at end of file
diff --git a/tests/topotests/mgmt_startup/r2/zebra.conf b/tests/topotests/mgmt_startup/r2/zebra.conf
new file mode 100644
index 0000000..1d37a65
--- /dev/null
+++ b/tests/topotests/mgmt_startup/r2/zebra.conf
@@ -0,0 +1,12 @@
+log timestamp precision 3
+debug northbound notifications
+debug northbound libyang
+debug northbound events
+debug northbound callbacks
+debug mgmt backend datastore frontend transaction
+debug mgmt client backend
+debug mgmt client frontend
+
+interface r2-eth0
+ ip address 101.0.0.2/24
+ ipv6 address 2101::2/64
diff --git a/tests/topotests/mgmt_startup/r3/zebra.conf b/tests/topotests/mgmt_startup/r3/zebra.conf
new file mode 100644
index 0000000..8419d74
--- /dev/null
+++ b/tests/topotests/mgmt_startup/r3/zebra.conf
@@ -0,0 +1,18 @@
+log timestamp precision 3
+debug northbound notifications
+debug northbound libyang
+debug northbound events
+debug northbound callbacks
+debug mgmt backend datastore frontend transaction
+debug mgmt client backend
+debug mgmt client frontend
+
+interface r3-eth0
+ ip address 101.0.0.3/24
+ ipv6 address 2101::3/64
+
+ip route 11.0.0.0/24 101.0.0.1
+ip route 12.0.0.0/24 101.0.0.2
+
+ipv6 route 2011::/48 2101::1
+ipv6 route 2012::/48 2101::2
diff --git a/tests/topotests/mgmt_startup/r4/frr.conf b/tests/topotests/mgmt_startup/r4/frr.conf
new file mode 100644
index 0000000..5f3b35d
--- /dev/null
+++ b/tests/topotests/mgmt_startup/r4/frr.conf
@@ -0,0 +1,21 @@
+log timestamp precision 6
+log file frr.log
+
+debug northbound notifications
+debug northbound libyang
+debug northbound events
+debug northbound callbacks
+debug mgmt backend datastore frontend transaction
+debug mgmt client backend
+debug mgmt client frontend
+
+interface r4-eth0
+ ip address 101.0.0.4/24
+ ipv6 address 2101::4/64
+exit
+
+ip route 11.0.0.0/24 101.0.0.1
+ip route 12.0.0.0/24 101.0.0.2
+
+ipv6 route 2012::/48 2101::2
+ipv6 route 2013::/48 2101::3
diff --git a/tests/topotests/mgmt_startup/test_bigconf.py b/tests/topotests/mgmt_startup/test_bigconf.py
new file mode 100644
index 0000000..4f46c8f
--- /dev/null
+++ b/tests/topotests/mgmt_startup/test_bigconf.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+#
+# May 2 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+"""
+Test static route startup functionality
+"""
+
+import datetime
+import logging
+import os
+
+import pytest
+from lib.common_config import step
+from lib.topogen import Topogen, TopoRouter
+from munet.base import Timeout
+from util import check_kernel, check_vtysh_up, write_big_route_conf
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd]
+pytestmark = [pytest.mark.staticd]
+
+
+track = Timeout(0)
+ROUTE_COUNT = 2500
+ROUTE_RANGE = [None, None]
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ global start_time
+ topodef = {
+ "s1": ("r1",),
+ }
+
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ prologue = open(f"{CWD}/r1/mgmtd.conf").read()
+
+ confpath = f"{tgen.gears['r1'].gearlogdir}/r1-late-big.conf"
+ start, end = write_big_route_conf("10.0.0.0/8", ROUTE_COUNT, confpath, prologue)
+ ROUTE_RANGE[0] = start
+ ROUTE_RANGE[1] = end
+
+ # configure mgmtd using current mgmtd config file
+ tgen.gears["r1"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ tgen.gears["r1"].load_config(TopoRouter.RD_MGMTD, confpath)
+
+ track.started_on = datetime.datetime.now()
+
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+def test_staticd_latestart(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.routers()["r1"]
+
+ step(f"Verifying {ROUTE_COUNT} startup routes are present")
+
+ check_vtysh_up(r1)
+ logging.info("r1: vtysh connected after %ss", track.elapsed())
+
+ result = check_kernel(r1, ROUTE_RANGE[0], retry_timeout=60)
+ assert result is None
+ logging.info("r1: first route installed after %ss", track.elapsed())
+
+ result = check_kernel(r1, ROUTE_RANGE[1], retry_timeout=60)
+ assert result is None
+ logging.info("r1: last route installed after %ss", track.elapsed())
diff --git a/tests/topotests/mgmt_startup/test_cfgfile_var.py b/tests/topotests/mgmt_startup/test_cfgfile_var.py
new file mode 100644
index 0000000..6a54f71
--- /dev/null
+++ b/tests/topotests/mgmt_startup/test_cfgfile_var.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+#
+# May 2 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+"""
+Test static route functionality using old or new configuration files.
+
+User compat:
+
+ - mgmtd split config will first look to `/etc/frr/zebra.conf`
+ then `/etc/frr/staticd.conf` and finally `/etc/frr/mgmtd.conf`
+
+ - When new components are converted to mgmtd their split config should be
+ added here too.
+
+Topotest compat:
+
+ - `mgmtd.conf` is copied to `/etc/frr/` for use by mgmtd when implicit load,
+ or explicit load no config specified.
+
+ - `staticd.conf` is copied to `/etc/frr/` for use by mgmtd when staticd
+ is explicit load implict config, and explicit config.
+
+"""
+
+import pytest
+from lib.common_config import step
+from lib.topogen import Topogen, TopoRouter
+from util import check_kernel
+
+# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd]
+pytestmark = [pytest.mark.staticd]
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ topodef = {
+ "s1": ("r1", "r2", "r3", "r4"),
+ }
+
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ # configure mgmtd using current mgmtd config file
+ tgen.gears["r1"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ tgen.gears["r1"].load_config(TopoRouter.RD_MGMTD, "mgmtd.conf")
+
+ # user/topotest compat:
+ # configure mgmtd using old staticd config file, with explicity staticd
+ # load.
+ tgen.gears["r2"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ tgen.gears["r2"].load_config(TopoRouter.RD_STATIC, "staticd.conf")
+
+ # user compat:
+ # configure mgmtd using backup config file `zebra.conf`
+ tgen.gears["r3"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+
+ # configure mgmtd using current mgmtd config file
+ tgen.gears["r4"].load_frr_config("frr.conf")
+
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+def test_staticd_routes_present(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for x in ["r1", "r2", "r3", "r4"]:
+ tgen.gears[x].net.cmd_nostatus(
+ "vtysh -c 'debug mgmt client frontend' "
+ "-c 'debug mgmt client backend' "
+ "-c 'debug mgmt backend frontend datastore transaction'"
+ )
+
+ r1 = tgen.routers()["r1"]
+ r2 = tgen.routers()["r2"]
+ r3 = tgen.routers()["r3"]
+ r4 = tgen.routers()["r4"]
+
+ step("Verifying routes are present on r1")
+ result = check_kernel(r1, "12.0.0.0/24")
+ assert result is None
+ result = check_kernel(r1, "13.0.0.0/24")
+ assert result is None
+
+ step("Verifying routes are present on r2")
+ result = check_kernel(r2, "11.0.0.0/24")
+ assert result is None
+ result = check_kernel(r2, "13.0.0.0/24")
+ assert result is None
+
+ step("Verifying routes are present on r3")
+ result = check_kernel(r3, "11.0.0.0/24")
+ assert result is None
+ result = check_kernel(r3, "12.0.0.0/24")
+ assert result is None
+
+ step("Verifying routes are present on r4")
+ result = check_kernel(r4, "11.0.0.0/24")
+ assert result is None
+ result = check_kernel(r4, "12.0.0.0/24")
+ assert result is None
diff --git a/tests/topotests/mgmt_startup/test_late_bigconf.py b/tests/topotests/mgmt_startup/test_late_bigconf.py
new file mode 100644
index 0000000..0b5bf38
--- /dev/null
+++ b/tests/topotests/mgmt_startup/test_late_bigconf.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+#
+# May 2 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+
+"""
+Verify large set of routes present when staticd (backend client) is started after it's
+startup config is present during launch.
+"""
+
+import logging
+import os
+
+import pytest
+from lib.common_config import step
+from lib.topogen import Topogen, TopoRouter
+from munet.base import Timeout
+from util import check_kernel, check_vtysh_up, write_big_route_conf
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd]
+pytestmark = [pytest.mark.staticd]
+
+track = Timeout(0)
+ROUTE_COUNT = 2500
+ROUTE_RANGE = [None, None]
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ global start_time
+ topodef = {
+ "s1": ("r1",),
+ }
+
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ prologue = open(f"{CWD}/r1/mgmtd.conf").read()
+
+ confpath = f"{tgen.gears['r1'].gearlogdir}/r1-late-big.conf"
+ start, end = write_big_route_conf("10.0.0.0/8", ROUTE_COUNT, confpath, prologue)
+ ROUTE_RANGE[0] = start
+ ROUTE_RANGE[1] = end
+
+ # configure mgmtd using current mgmtd config file
+ tgen.gears["r1"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ tgen.gears["r1"].load_config(TopoRouter.RD_MGMTD, confpath)
+
+ # Explicit disable staticd now..
+ tgen.gears["r1"].net.daemons["staticd"] = 0
+
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+def test_staticd_latestart(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.routers()["r1"]
+
+ check_vtysh_up(r1)
+ logging.info("r1: vtysh connected after %ss", track.elapsed())
+
+ result = check_kernel(r1, ROUTE_RANGE[0], retry_timeout=60, expected=False)
+ assert result is not None, "first route present and should not be"
+ result = check_kernel(r1, ROUTE_RANGE[1], retry_timeout=60, expected=False)
+ assert result is not None, "last route present and should not be"
+
+ step("Starting staticd")
+ t2 = Timeout(0)
+ r1.startDaemons(["staticd"])
+
+ result = check_kernel(r1, ROUTE_RANGE[0], retry_timeout=60)
+ assert result is None, "first route not present and should be"
+ logging.info("r1: elapsed time for first route %ss", t2.elapsed())
+
+ count = 0
+ ocount = 0
+ while count < ROUTE_COUNT:
+ rc, o, e = r1.net.cmd_status("ip -o route | wc -l")
+ if not rc:
+ if count > ocount + 100:
+ ocount = count
+ logging.info("r1: elapsed time for %d routes %s", count, t2.elapsed())
+ count = int(o)
+
+ result = check_kernel(r1, ROUTE_RANGE[1], retry_timeout=1200)
+ assert result is None, "last route not present and should be"
+ logging.info("r1: elapsed time for last route %ss", t2.elapsed())
diff --git a/tests/topotests/mgmt_startup/test_late_uniconf.py b/tests/topotests/mgmt_startup/test_late_uniconf.py
new file mode 100644
index 0000000..d4e7e07
--- /dev/null
+++ b/tests/topotests/mgmt_startup/test_late_uniconf.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+#
+# May 2 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+
+"""
+Verify routes present when staticd (backend client) is started after it's startup
+config, contained inside a unified configuration file, is present during launch.
+"""
+import pytest
+from lib.topogen import Topogen
+from util import _test_staticd_late_start
+
+# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd]
+pytestmark = [pytest.mark.staticd]
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ topodef = {
+ "s1": ("r4",),
+ }
+
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ # configure mgmtd using current mgmtd config file
+ tgen.gears["r4"].load_frr_config("frr.conf")
+
+ # Explicit disable staticd now..
+ tgen.gears["r4"].net.daemons["staticd"] = 0
+
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+def test_staticd_late_start(tgen):
+ return _test_staticd_late_start(tgen, tgen.routers()["r4"])
diff --git a/tests/topotests/mgmt_startup/test_latestart.py b/tests/topotests/mgmt_startup/test_latestart.py
new file mode 100644
index 0000000..1c97b9d
--- /dev/null
+++ b/tests/topotests/mgmt_startup/test_latestart.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+#
+# May 2 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+"""
+Verify routes present when staticd (backend client) is started after it's startup config
+is present during launch.
+"""
+
+import pytest
+from lib.topogen import Topogen, TopoRouter
+from util import _test_staticd_late_start
+
+# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd]
+pytestmark = [pytest.mark.staticd]
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ topodef = {
+ "s1": ("r1",),
+ }
+
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ # configure mgmtd using current mgmtd config file
+ tgen.gears["r1"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ tgen.gears["r1"].load_config(TopoRouter.RD_MGMTD)
+
+ # Explicit disable staticd now..
+ tgen.gears["r1"].net.daemons["staticd"] = 0
+
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+def test_staticd_late_start(tgen):
+ return _test_staticd_late_start(tgen, tgen.routers()["r1"])
diff --git a/tests/topotests/mgmt_startup/util.py b/tests/topotests/mgmt_startup/util.py
new file mode 100644
index 0000000..e366351
--- /dev/null
+++ b/tests/topotests/mgmt_startup/util.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+#
+# May 28 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+
+import ipaddress
+import math
+import re
+
+import pytest
+from lib.common_config import retry, step
+from lib.topolog import logger
+from munet.base import proc_error
+
+
+@retry(retry_timeout=30)
+def check_vtysh_up(router):
+ rc, o, e = router.net.cmd_status("vtysh -c 'show version'")
+ return None if not rc else proc_error(rc, o, e)
+
+
+@retry(retry_timeout=3, initial_wait=0.1)
+def check_kernel(r1, prefix, expected=True):
+ net = ipaddress.ip_network(prefix)
+ if net.version == 6:
+ kernel = r1.net.cmd_nostatus("ip -6 route show", warn=not expected)
+ else:
+ kernel = r1.net.cmd_nostatus("ip -4 route show", warn=not expected)
+
+ logger.debug("checking kernel routing table:\n%0.1920s", kernel)
+ route = f"{str(net)}(?: nhid [0-9]+)?.*proto (static|196)"
+ m = re.search(route, kernel)
+ if expected and not m:
+ return f"Failed to find \n'{route}'\n in \n'{kernel:.1920}'"
+ elif not expected and m:
+ return f"Failed found \n'{route}'\n in \n'{kernel:.1920}'"
+ return None
+
+
+def get_ip_networks(super_prefix, count):
+ count_log2 = math.log(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 write_big_route_conf(super_prefix, count, confpath, prologue=""):
+ start = None
+ end = None
+
+ with open(confpath, "w+", encoding="ascii") as f:
+ if prologue:
+ f.write(prologue + "\n")
+ for net in get_ip_networks(super_prefix, count):
+ end = net
+ if not start:
+ start = net
+ f.write(f"ip route {net} lo\n")
+
+ return start, end
+
+
+def _test_staticd_late_start(tgen, router):
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # for x in ["r1"]:
+ # tgen.gears[x].net.cmd_nostatus(
+ # "vtysh -c 'debug mgmt client frontend' "
+ # "-c 'debug mgmt client backend' "
+ # "-c 'debug mgmt backend frontend datastore transaction'"
+ # )
+
+ step("Verifying startup route is not present w/o staticd running")
+ result = check_kernel(router, "12.0.0.0/24", expected=False)
+ assert result is not None
+
+ step("Configure another static route verify is not present w/o staticd running")
+ router.net.cmd_nostatus("vtysh -c 'config t' -c 'ip route 12.1.0.0/24 101.0.0.2'")
+ result = check_kernel(router, "12.0.0.0/24", expected=False)
+ assert result is not None
+ result = check_kernel(router, "12.1.0.0/24", expected=False)
+ assert result is not None
+
+ step("Starting staticd")
+ router.startDaemons(["staticd"])
+
+ step("Verifying both routes are present")
+ result = check_kernel(router, "12.0.0.0/24")
+ assert result is None
+ result = check_kernel(router, "12.1.0.0/24")
+ assert result is None
diff --git a/tests/topotests/mgmt_tests/test_yang_mgmt.py b/tests/topotests/mgmt_tests/test_yang_mgmt.py
new file mode 100644
index 0000000..921b4e6
--- /dev/null
+++ b/tests/topotests/mgmt_tests/test_yang_mgmt.py
@@ -0,0 +1,548 @@
+#!/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.
+#
+"""
+
+1. Verify mgmt commit check.
+2. Verify mgmt commit apply.
+3. Verify mgmt commit abort.
+4. Verify mgmt delete config.
+5. Kill mgmtd - verify that static routes are intact.
+6. Kill mgmtd - verify that watch frr restarts.
+7. Show and CLI - Execute all the newly introduced commands of mgmtd.
+8. Verify mgmt rollback functionality.
+
+"""
+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,
+ apply_raw_config,
+ kill_router_daemons,
+ start_router_daemons,
+)
+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 = "{}/yang_mgmt.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 deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), "4.19") < 0:
+ error_msg = (
+ 'These tests will not run. (have kernel "{}", '
+ "requires kernel >= 4.19)".format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # 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: %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_mgmt_commit_check(request):
+ """
+ Verify mgmt commit check.
+
+ """
+ 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("Mgmt Commit check")
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec",
+ "mgmt commit check",
+ ]
+ }
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Mgmt Commit check")
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec",
+ "mgmt commit check",
+ ]
+ }
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify that the route is not configured, as commit apply not done.")
+
+ dut = "r1"
+ protocol = "static"
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": "1192.1.1.2/32",
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_mgmt_commit_apply(request):
+ """
+ Verify mgmt commit apply.
+
+ """
+ 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("Mgmt Commit apply with Valid Configuration")
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default",
+ "mgmt commit apply",
+ ]
+ }
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Mgmt Commit apply with Invalid Configuration")
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default",
+ "mgmt commit apply",
+ ]
+ }
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify that the route is configured")
+
+ dut = "r1"
+ protocol = "static"
+ input_dict_4 = {"r2": {"static_routes": [{"network": "192.1.1.20/32"}]}}
+ result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_mgmt_commit_abort(request):
+ """
+ Verify mgmt commit abort.
+
+ """
+ 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("Mgmt Commit abort")
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.3/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default",
+ "mgmt commit abort",
+ ]
+ }
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify that the route is not configured")
+
+ dut = "r1"
+ protocol = "static"
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": "192.1.1.3/32",
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_mgmt_delete_config(request):
+ """
+ Verify mgmt delete config.
+
+ """
+ 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("Mgmt - Configure a static route using commit apply")
+
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default",
+ "mgmt commit apply",
+ ]
+ }
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify that the route is added to RIB")
+ dut = "r1"
+ protocol = "static"
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": "192.168.1.3/32",
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Mgmt delete config")
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][afi-safi='frr-routing:ipv4-unicast']",
+ "mgmt commit apply",
+ ]
+ }
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify that the route is deleted from RIB")
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes is still present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_mgmt_chaos_stop_start_frr(request):
+ """
+ Kill mgmtd - verify that watch frr restarts.
+
+ """
+ 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 Static route with next hop null 0")
+
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec",
+ "mgmt commit apply",
+ ]
+ }
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify that the route is configured and present in the zebra")
+
+ dut = "r1"
+ protocol = "static"
+ input_dict_4 = {"r2": {"static_routes": [{"network": "192.1.11.200/32"}]}}
+ result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Restart frr")
+ stop_router(tgen, "r1")
+ start_router(tgen, "r1")
+ step("Verify routes are intact in zebra.")
+ result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("delete the configured route and ")
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']",
+ "mgmt commit apply",
+ ]
+ }
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify that the route is deleted and deleted from zebra")
+
+ dut = "r1"
+ protocol = "static"
+ input_dict_4 = {"r1": {"static_routes": [{"network": "192.1.11.200/32"}]}}
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_4, 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_mgmt_chaos_kill_daemon(request):
+ """
+ Kill mgmtd - verify that static routes are intact
+
+ """
+ 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 Static route with next hop null 0")
+
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec",
+ "mgmt commit apply",
+ ]
+ }
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify that the route is configured and present in the zebra")
+
+ dut = "r1"
+ protocol = "static"
+ input_dict_4 = {"r2": {"static_routes": [{"network": "192.1.11.200/32"}]}}
+ result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Kill static daemon on R2.")
+ kill_router_daemons(tgen, "r1", ["staticd"])
+
+ step("Bring up staticd daemon on R2.")
+ start_router_daemons(tgen, "r1", ["staticd"])
+
+ step("Verify routes are intact in zebra.")
+ result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Kill mgmt daemon on R2.")
+ kill_router_daemons(tgen, "r1", ["mgmtd"])
+
+ step("Bring up zebra daemon on R2.")
+ start_router_daemons(tgen, "r1", ["mgmtd"])
+
+ step("Verify routes are intact in zebra.")
+ result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes 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/mgmt_tests/yang_mgmt.json b/tests/topotests/mgmt_tests/yang_mgmt.json
new file mode 100644
index 0000000..0fe3bb1
--- /dev/null
+++ b/tests/topotests/mgmt_tests/yang_mgmt.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
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
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..6d9304d
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py
@@ -0,0 +1,230 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_msdp_mesh_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (C) 2021 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..1af58b0
--- /dev/null
+++ b/tests/topotests/msdp_topo1/test_msdp_topo1.py
@@ -0,0 +1,442 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_msdp_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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_mld_join_topo1/multicast_mld_local_join.json b/tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json
new file mode 100644
index 0000000..d7053bf
--- /dev/null
+++ b/tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json
@@ -0,0 +1,249 @@
+{
+ "address_types": ["ipv6"],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "r4": {"ipv6": "auto", "pim6": "enable"},
+ "r2": {"ipv6": "auto", "pim6": "enable"},
+ "r3": {"ipv6": "auto", "pim6": "enable"},
+ "i1": {"ipv6": "auto", "pim6": "enable"},
+ "i2": {"ipv6": "auto", "pim6": "enable"}
+ },
+
+ "bgp": {
+ "local_as": "100",
+ "router_id": "192.168.1.1",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"},
+ "r1": {"ipv6": "auto", "pim6": "enable"},
+ "r4": {"ipv6": "auto", "pim6": "enable"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "router_id": "192.168.1.2",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "r1": {"ipv6": "auto", "pim6": "enable"},
+ "r4": {"ipv6": "auto", "pim6": "enable"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "router_id": "192.168.1.3",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "r2": {"ipv6": "auto", "pim6": "enable"},
+ "r3": {"ipv6": "auto", "pim6": "enable"},
+ "i4": {"ipv6": "auto", "pim6": "enable"},
+ "r1": {"ipv6": "auto", "pim6": "enable"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "router_id": "192.168.1.4",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "i1": {
+ "links": {
+ "r1": {"ipv6": "auto"}
+ }
+ },
+ "i2": {
+ "links": {
+ "r1": {"ipv6": "auto"}
+ }
+ },
+ "i4": {
+ "links": {
+ "r4": {"ipv6": "auto"}
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py b/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py
new file mode 100644
index 0000000..84a13ae
--- /dev/null
+++ b/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py
@@ -0,0 +1,926 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2023 by VMware, Inc. ("VMware")
+#
+
+"""
+Following tests are covered to test_multicast_pim_mld_local_tier_1:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+
+1. Verify static MLD group populated when static "ip mld join <grp>" in configured
+2. Verify mroute and upstream populated with correct OIL/IIF with static imld join
+3. Verify local MLD join not allowed for non multicast group
+4. Verify static MLD group removed from DUT while removing "ip mld join" CLI
+5. Verify static MLD groups after removing and adding MLD config
+"""
+
+import sys
+import time
+
+import pytest
+from lib.common_config import (
+ reset_config_on_routers,
+ start_topology,
+ step,
+ write_test_footer,
+ write_test_header,
+)
+from lib.pim import (
+ McastTesterHelper,
+ create_mld_config,
+ create_pim_config,
+ verify_local_mld_groups,
+ verify_mld_groups,
+ verify_mroutes,
+ verify_pim_neighbors,
+ verify_pim_rp_info,
+ verify_upstream_iif,
+)
+from lib.bgp import (
+ verify_bgp_convergence,
+)
+
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+
+r1_r2_links = []
+r1_r3_links = []
+r2_r1_links = []
+r2_r4_links = []
+r3_r1_links = []
+r3_r4_links = []
+r4_r2_links = []
+r4_r3_links = []
+
+pytestmark = [pytest.mark.pim6d, pytest.mark.staticd]
+
+TOPOLOGY = """
+ +-------------------+
+ | |
+ i1--- R1-------R2----------R4---i2
+ | |
+ +-------R3----------+
+
+
+ Description:
+ i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send MLD
+ join and traffic
+ R1 - DUT (LHR)
+ R2 - RP
+ R3 - Transit
+ R4 - (FHR)
+
+"""
+# Global variables
+
+GROUP_RANGE = "ffaa::/16"
+RP_RANGE = "ff00::/8"
+GROUP_RANGE_1 = [
+ "ffaa::1/128",
+ "ffaa::2/128",
+ "ffaa::3/128",
+ "ffaa::4/128",
+ "ffaa::5/128",
+]
+MLD_JOIN_RANGE_1 = ["ffaa::1", "ffaa::2", "ffaa::3", "ffaa::4", "ffaa::5"]
+MLD_JOIN_RANGE_2 = [
+ "ff02::1:ff00:0",
+ "ff02::d",
+ "fe80::250:56ff:feb7:d8d5",
+ "2001::4",
+ "2002::5",
+]
+
+
+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("Master Topology: \n {}".format(TOPOLOGY))
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ json_file = "multicast_mld_local_join.json"
+ 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)
+
+ # 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 BGP convergence
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ # Verify PIM neighbors
+ result = verify_pim_neighbors(tgen, topo)
+ assert result is True, " Verify PIM neighbor: Failed Error: {}".format(result)
+
+ 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 test_mld_local_joins_p0(request):
+ """
+ Verify static MLD group populated when static
+ "ipv6 mld join <grp>" in 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)
+
+ reset_config_on_routers(tgen)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected")
+ step("Enable the MLD on R11 interfac of R1 and configure local mld groups")
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"]
+ input_dict = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}},
+ intf_r1_i2: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}},
+ }
+ }
+ }
+ }
+
+ result = create_mld_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (ffaa::1-5) as R2")
+
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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("verify static mld join using show ipv6 mld join")
+ dut = "r1"
+ interfaces = [intf_r1_i1, intf_r1_i2]
+ for interface in interfaces:
+ result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify mld groups using show ipv6 mld groups")
+ interfaces = [intf_r1_i1, intf_r1_i2]
+ for interface in interfaces:
+ result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_mroute_with_mld_local_joins_p0(request):
+ """
+ Verify mroute and upstream populated with correct OIL/IIF with
+ static mld join
+ """
+ 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ app_helper.stop_all_hosts()
+
+ step("Enable the PIM on all the interfaces of R1, R2, R3, R4")
+ step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected")
+ step("Enable the MLD on R11 interfac of R1 and configure local mld groups")
+
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"]
+ input_dict = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}},
+ intf_r1_i2: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}},
+ }
+ }
+ }
+ }
+
+ result = create_mld_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (ffaa::1-5) as R2")
+
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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("verify static mld join using show ipv6 mld join")
+ dut = "r1"
+ interfaces = [intf_r1_i1, intf_r1_i2]
+ for interface in interfaces:
+ result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify mld groups using show ipv6 mld groups")
+ interfaces = [intf_r1_i1, intf_r1_i2]
+ for interface in interfaces:
+ result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify RP-info populated in DUT")
+ dut = "r1"
+ rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
+ SOURCE = "Static"
+ oif = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)")
+ result = app_helper.run_traffic("i4", MLD_JOIN_RANGE_1, "r4")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "'show ipv6 mroute' showing correct RPF and OIF interface for (*,G)"
+ " and (S,G) entries on all the nodes"
+ )
+ source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0]
+
+ intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"]
+
+ input_dict_starg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": intf_r1_r2,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": intf_r1_r2,
+ "oil": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ },
+ ]
+
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream for local mld groups")
+ for input_dict in [input_dict_starg, input_dict_sg]:
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_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"], MLD_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify mroutes not created with local interface ip ")
+ input_dict_local_sg = [
+ {
+ "dut": "r1",
+ "src_address": intf_r1_i1,
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": intf_r1_i2,
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ },
+ ]
+
+ for data in input_dict_local_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed Error: {}"
+ "sg created with local interface ip".format(tc_name, result)
+ )
+
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed Error: {}"
+ "upstream created with local interface ip".format(tc_name, result)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_remove_add_mld_local_joins_p1(request):
+ """
+ Verify static MLD group removed from DUT while
+ removing "ip mld join" CLI
+ """
+
+ 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ app_helper.stop_all_hosts()
+
+ step("Enable the PIM on all the interfaces of R1, R2, R3, R4")
+ step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected")
+ step("Enable the MLD on R11 interfac of R1 and configure local mld groups")
+
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+
+ input_dict = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}
+ }
+ }
+ }
+ }
+
+ result = create_mld_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (ffaa::1-5) as R2")
+
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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("verify static mld join using show ipv6 mld join")
+ dut = "r1"
+ interface = intf_r1_i1
+ result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("verify mld groups using show ipv6 mld groups")
+
+ interface = intf_r1_i1
+ result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("verify RP-info populated in DUT")
+ dut = "r1"
+ rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
+ SOURCE = "Static"
+ oif = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)")
+ result = app_helper.run_traffic("i4", MLD_JOIN_RANGE_1, "r4")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "'show ipv6 mroute' showing correct RPF and OIF interface for (*,G)"
+ " and (S,G) entries on all the nodes"
+ )
+ source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0]
+
+ intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ input_dict_starg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": intf_r1_r2,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ }
+ ]
+
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ }
+ ]
+
+ step("Verify mroutes and iff upstream for local mld groups")
+ for input_dict in [input_dict_starg, input_dict_sg]:
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_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"], MLD_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove MLD join from DUT")
+ input_dict = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ intf_r1_i1: {
+ "mld": {
+ "join": MLD_JOIN_RANGE_1,
+ "delete_attr": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_mld_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("verify static mld join removed using show ipv6 mld join")
+ dut = "r1"
+ interface = intf_r1_i1
+ result = verify_local_mld_groups(
+ tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Error: {}" "MLD join still present".format(
+ tc_name, result
+ )
+
+ step("verify mld groups removed using show ipv6 mld groups")
+ interface = intf_r1_i1
+ result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Error: {}" "MLD groups still present".format(
+ tc_name, result
+ )
+
+ step("Verify mroutes and iff upstream for local mld groups")
+ for input_dict in [input_dict_starg, input_dict_sg]:
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error: {}" "mroutes still present".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error: {}" "mroutes still present".format(
+ tc_name, result
+ )
+
+ step("Add MLD join on DUT again")
+ input_dict = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ intf_r1_i1: {
+ "mld": {
+ "join": MLD_JOIN_RANGE_1,
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_mld_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("verify static mld join using show ipv6 mld join")
+ dut = "r1"
+ interface = intf_r1_i1
+ result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("verify mld groups using show ipv6 mld groups")
+
+ interface = intf_r1_i1
+ result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify mroutes and iff upstream for local mld groups")
+ for input_dict in [input_dict_starg, input_dict_sg]:
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_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"], MLD_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_remove_add_mld_config_with_local_joins_p1(request):
+ """
+ Verify static MLD groups after removing
+ and adding MLD config
+ """
+
+ 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ app_helper.stop_all_hosts()
+
+ step("Enable the PIM on all the interfaces of R1, R2, R3, R4")
+ step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected")
+ step("Enable the MLD on R11 interfac of R1 and configure local mld groups")
+
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_dict = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}
+ }
+ }
+ }
+ }
+
+ result = create_mld_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (ffaa::1-5) as R2")
+
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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("verify static mld join using show ipv6 mld join")
+ dut = "r1"
+ interface = intf_r1_i1
+ result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("verify mld groups using show ipv6 mld groups")
+ interface = intf_r1_i1
+ result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)")
+ result = app_helper.run_traffic("i4", MLD_JOIN_RANGE_1, "r4")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "'show ipv6 mroute' showing correct RPF and OIF interface for (*,G)"
+ " and (S,G) entries on all the nodes"
+ )
+ source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0]
+
+ intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ input_dict_starg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": intf_r1_r2,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ }
+ ]
+
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ }
+ ]
+
+ step("Verify mroutes and iff upstream for local mld groups")
+ for input_dict in [input_dict_starg, input_dict_sg]:
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_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"], MLD_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove mld and mld version 2 from DUT interface")
+ input_dict = {
+ "r1": {
+ "mld": {
+ "interfaces": {intf_r1_i1: {"mld": {"version": "1", "delete": True}}}
+ }
+ }
+ }
+
+ result = create_mld_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("verify static mld join using show ipv6 mld join")
+ dut = "r1"
+ interface = intf_r1_i1
+ result = verify_local_mld_groups(
+ tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify mld groups using show ipv6 mld groups")
+ interface = intf_r1_i1
+ result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Error: {}" "MLD grsp still present".format(
+ tc_name, result
+ )
+
+ step("Verify mroutes and iff upstream for local mld groups")
+ for input_dict in [input_dict_starg, input_dict_sg]:
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error: {}" "mroutes still present".format(
+ tc_name, result
+ )
+
+ step("Add mld and mld version 2 from DUT interface")
+ input_dict = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}
+ }
+ }
+ }
+ }
+
+ result = create_mld_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("verify static mld join using show ipv6 mld join")
+ dut = "r1"
+ interface = intf_r1_i1
+ result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("verify mld groups using show ipv6 mld groups")
+ interface = intf_r1_i1
+ result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify mroutes and iff upstream for local mld groups")
+ for input_dict in [input_dict_starg, input_dict_sg]:
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_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"], MLD_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_pim6_sm_topo1/multicast_pim6_sm_topo1.json b/tests/topotests/multicast_pim6_sm_topo1/multicast_pim6_sm_topo1.json
new file mode 100644
index 0000000..341cd5a
--- /dev/null
+++ b/tests/topotests/multicast_pim6_sm_topo1/multicast_pim6_sm_topo1.json
@@ -0,0 +1,300 @@
+{
+ "address_types": ["ipv6"],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"},
+ "r2": {"ipv6": "auto", "pim6": "enable"},
+ "r4": {"ipv6": "auto", "pim6": "enable"},
+ "i1": {"ipv6": "auto", "pim6": "enable"},
+ "i6": {"ipv6": "auto", "pim6": "enable"},
+ "i7": {"ipv6": "auto", "pim6": "enable"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "router_id": "192.168.1.1",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "mld": {
+ "interfaces": {
+ "r1-i1-eth0" :{
+ "mld":{
+ "version": 1
+ }
+ }
+ }
+ }
+ },
+
+ "r2": {
+ "links": {
+ "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"},
+ "r1": {"ipv6": "auto", "pim6": "enable"},
+ "r3": {"ipv6": "auto", "pim6": "enable"},
+ "i3": {"ipv6": "auto", "pim6": "enable"}
+ },
+ "bgp": {
+ "local_as": "200",
+ "router_id": "192.168.1.2",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"},
+ "r2": {"ipv6": "auto", "pim6": "enable"},
+ "r5": {"ipv6": "auto", "pim6": "enable"},
+ "i2": {"ipv6": "auto", "pim6": "enable"},
+ "i8": {"ipv6": "auto", "pim6": "enable"}
+ },
+ "bgp": {
+ "local_as": "300",
+ "router_id": "192.168.1.3",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"},
+ "r1": {"ipv6": "auto", "pim6": "enable"},
+ "r5": {"ipv6": "auto", "pim6": "enable"},
+ "i4": {"ipv6": "auto", "pim6": "enable"}
+ },
+ "bgp": {
+ "local_as": "400",
+ "router_id": "192.168.1.4",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ "r5": {
+ "links": {
+ "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"},
+ "r3": {"ipv6": "auto", "pim6": "enable"},
+ "r4": {"ipv6": "auto", "pim6": "enable"},
+ "i5": {"ipv6": "auto", "pim6": "enable"}
+ },
+ "bgp": {
+ "local_as": "500",
+ "router_id": "192.168.1.5",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ "i1": {
+ "links": {
+ "r1": {"ipv6": "auto"}
+ }
+ },
+ "i2": {
+ "links": {
+ "r3": {"ipv6": "auto"}
+ }
+ },
+ "i3": {
+ "links": {
+ "r2": {"ipv6": "auto"}
+ }
+ },
+ "i4": {
+ "links": {
+ "r4": {"ipv6": "auto"}
+ }
+ },
+ "i5": {
+ "links": {
+ "r5": {"ipv6": "auto"}
+ }
+ },
+ "i6": {
+ "links": {
+ "r1": {"ipv6": "auto"}
+ }
+ },
+ "i7": {
+ "links": {
+ "r1": {"ipv6": "auto"}
+ }
+ },
+ "i8": {
+ "links": {
+ "r3": {"ipv6": "auto"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py
new file mode 100644
index 0000000..7eb5838
--- /dev/null
+++ b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py
@@ -0,0 +1,1595 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+Following tests are covered to test multicast pim6 sm:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+1. Verify Multicast data traffic with static RP, (*,g) and
+(s,g) OIL updated correctly
+2. Verify mroute detail when receiver is present
+outside of FRR
+3. Verify (*,G) and (S,G) populated correctly
+when FRR is the transit router
+4. Verify (S,G) should not create if RP is not reachable
+5. Verify modification of mld query timer should get update
+accordingly
+6. Verify modification of mld max query response timer
+should get update accordingly
+7. Verify removing the RP should not impact the multicast
+data traffic
+"""
+
+import datetime
+import sys
+import time
+
+import pytest
+from lib.common_config import (
+ get_frr_ipv6_linklocal,
+ required_linux_kernel_version,
+ reset_config_on_routers,
+ shutdown_bringup_interface,
+ start_topology,
+ step,
+ write_test_footer,
+ write_test_header,
+)
+from lib.pim import (
+ McastTesterHelper,
+ clear_pim6_mroute,
+ create_mld_config,
+ create_pim_config,
+ verify_mld_config,
+ verify_mld_groups,
+ verify_mroute_summary,
+ verify_mroutes,
+ verify_pim_interface_traffic,
+ verify_pim_join,
+ verify_pim_nexthop,
+ verify_pim_state,
+ verify_sg_traffic,
+ verify_upstream_iif,
+)
+from lib.bgp import (
+ verify_bgp_convergence,
+)
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+
+# Global variables
+GROUP_RANGE = "ff00::/8"
+
+GROUP_RANGE_1 = [
+ "ffaa::1/128",
+ "ffaa::2/128",
+ "ffaa::3/128",
+ "ffaa::4/128",
+ "ffaa::5/128",
+]
+MLD_JOIN_RANGE_1 = ["ffaa::1", "ffaa::2", "ffaa::3", "ffaa::4", "ffaa::5"]
+
+GROUP_RANGE_2 = [
+ "ffbb::1/128",
+ "ffbb::2/128",
+ "ffbb::3/128",
+ "ffbb::4/128",
+ "ffbb::5/128",
+]
+MLD_JOIN_RANGE_2 = ["ffbb::1", "ffbb::2", "ffbb::3", "ffbb::4", "ffbb::5"]
+GROUP_RANGE_3 = [
+ "ffcc::1/128",
+ "ffcc::2/128",
+ "ffcc::3/128",
+ "ffcc::4/128",
+ "ffcc::5/128",
+]
+MLD_JOIN_RANGE_3 = ["ffcc::1", "ffcc::2", "ffcc::3", "ffcc::4", "ffcc::5"]
+
+HELLO_TIMER = 1
+HOLD_TIMER = 3
+PREFERRED_NEXT_HOP = "link_local"
+ASSERT_MSG = "Testcase {} : Failed Error: {}"
+
+pytestmark = [pytest.mark.pim6d]
+
+
+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")
+
+ json_file = "multicast_pim6_sm_topo1.json"
+ 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 deamons and then start routers
+ start_topology(tgen)
+
+ # 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)
+
+ global app_helper
+ app_helper = McastTesterHelper(tgen)
+
+ # Verify BGP convergence
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ 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()
+
+ 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 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 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 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_multicast_data_traffic_static_RP_send_traffic_then_join_p0(request):
+ """
+ Verify Multicast data traffic with static RP, (*,g) and
+ (s,g) OIL updated correctly
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ app_helper.stop_all_hosts()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("shut R1 to R4 and R3 to R5 link to simulate test topology")
+ r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"]
+ r3_r5 = topo["routers"]["r3"]["links"]["r5"]["interface"]
+
+ shutdown_bringup_interface(tgen, "r1", r1_r4, False)
+ shutdown_bringup_interface(tgen, "r3", r3_r5, False)
+
+ step(
+ "Configure RP as R2 (loopback interface) for the"
+ " group range ff00::/8 on all the routers"
+ )
+
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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 mld join")
+
+ step("Send multicast traffic from FRR3 to all the receivers" "ffaa::1-5")
+
+ result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0]
+
+ step("verify upstream in NOT join Rej prune state on R3")
+
+ input_dict_sg = [
+ {
+ "dut": "r3",
+ "src_address": source,
+ "iif": topo["routers"]["r3"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r3"]["links"]["r2"]["interface"],
+ }
+ ]
+
+ for data in input_dict_sg:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ joinState="NotJoined",
+ regState="RegPrune",
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("joinRx value before join sent")
+ r2_r1_intf = topo["routers"]["r2"]["links"]["r1"]["interface"]
+ state_dict = {"r2": {r2_r1_intf: ["joinRx"]}}
+ 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 (ffaa::1-5) to R1")
+ result = app_helper.run_join("i1", MLD_JOIN_RANGE_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ipv6 mroute' showing correct RPF and OIF"
+ " interface for (*,G) and (S,G) entries on all the nodes"
+ )
+
+ input_dict = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source,
+ "iif": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r2"]["links"]["r1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": source,
+ "iif": topo["routers"]["r2"]["links"]["r3"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["r1"]["interface"],
+ },
+ {
+ "dut": "r3",
+ "src_address": source,
+ "iif": topo["routers"]["r3"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r3"]["links"]["r2"]["interface"],
+ },
+ ]
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ipv6 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"], MLD_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify join state is joined")
+ for data in input_dict_sg:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ joinState="Joined",
+ regState="RegPrune",
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("joinRx value after join sent")
+ 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
+ )
+
+ step(
+ "r1 sent PIM (*,G) join to r2 verify using"
+ "'show ipv6 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("r1 sent PIM (S,G) join to r3 , verify using 'show ipv6 pim join'")
+ dut = "r3"
+ interface = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ result = verify_pim_join(tgen, topo, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ _nexthop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0]
+ next_hop = next_hop_per_address_family(tgen, "r1", "r2", "ipv6", _nexthop)
+
+ step("verify nexthop on r3 using 'show ipv6 pim nexthop'")
+ result = verify_pim_nexthop(tgen, topo, "r1", next_hop, "ipv6")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify mroute summary on r1 using 'show ipv6 mroute summary json'")
+ result = verify_mroute_summary(
+ tgen, "r1", sg_mroute=5, starg_mroute=5, total_mroute=10, addr_type="ipv6"
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_when_receiver_is_outside_frr_p0(request):
+ """
+ Verify mroute detail when receiver is present
+ outside of FRR
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure static RP on r4 for group range " "(ffcc::1-5) and (ffbb::1-5)")
+
+ _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3
+ _MLD_JOIN_RANGE = MLD_JOIN_RANGE_2 + MLD_JOIN_RANGE_3
+
+ input_dict = {
+ "r4": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv6"].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 mld join (ffaa::1-5) to R1")
+ result = app_helper.run_join("i1", _MLD_JOIN_RANGE, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("verify MLD joins received on r1")
+ dut = "r1"
+ interface = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ result = verify_mld_groups(tgen, dut, interface, _MLD_JOIN_RANGE)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to all the receivers" "ffaa::1-5")
+ result = app_helper.run_traffic("i2", _MLD_JOIN_RANGE, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure one more receiver in r5 enable mld and send"
+ " join (ffaa::1-5) and (ffbb::1-5)"
+ )
+ r5_i5 = topo["routers"]["r5"]["links"]["i5"]["interface"]
+
+ input_dict = {"r5": {"mld": {"interfaces": {r5_i5: {"mld": {"version": "1"}}}}}}
+ result = create_mld_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = app_helper.run_join("i5", _MLD_JOIN_RANGE, "r5")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("FRR1 has 10 (*.G) and 10 (S,G) verify using 'show ipv6 mroute'")
+ step(
+ "All the receiver are receiving traffic on FRR1 and (S,G) OIL is towards"
+ "receivers, verify using 'show mroute' 'show pim upstream'"
+ )
+ step(
+ "All the receiver are receiving traffic on r5 and (S,G) OIL is "
+ "toward receivers, verify using 'show ipv6 mroute' 'show ipv6 pim upstream'"
+ )
+
+ source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0]
+ input_dict = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source,
+ "iif": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r5",
+ "src_address": "*",
+ "iif": topo["routers"]["r5"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r5"]["links"]["i5"]["interface"],
+ },
+ {
+ "dut": "r5",
+ "src_address": source,
+ "iif": topo["routers"]["r5"]["links"]["r3"]["interface"],
+ "oil": topo["routers"]["r5"]["links"]["i5"]["interface"],
+ },
+ ]
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _MLD_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 r2/r5 receiver and FRR1 receiver"
+ "'show ipv6 pim '"
+ )
+ input_dict = [
+ {
+ "dut": "r3",
+ "src_address": source,
+ "iif": topo["routers"]["r3"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r3"]["links"]["r5"]["interface"],
+ },
+ {
+ "dut": "r3",
+ "src_address": source,
+ "iif": topo["routers"]["r3"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r3"]["links"]["r2"]["interface"],
+ },
+ ]
+ for data in input_dict:
+ result = verify_pim_state(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["oil"],
+ _MLD_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"],
+ _MLD_JOIN_RANGE,
+ joinState="Joined",
+ regState="RegPrune",
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Traffic is received fine on FRR1 and r5 " " 'show ipv6 mroute count' ")
+
+ result = verify_sg_traffic(tgen, "r1", _MLD_JOIN_RANGE, source, "ipv6")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_sg_traffic(tgen, "r5", _MLD_JOIN_RANGE, source, "ipv6")
+ 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):
+ """
+ Verify (*,G) and (S,G) populated correctly
+ when FRR is the transit router
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ app_helper.stop_all_hosts()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure static RP for (ffaa::1-5) in r5")
+ input_dict = {
+ "r5": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r5"]["links"]["lo"]["ipv6"].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 mld on FRR1 interface and send mld join ")
+
+ step("send mld join (ffaa::1-5) to R1")
+ result = app_helper.run_join("i1", MLD_JOIN_RANGE_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("verify mld groups received on R1")
+ dut = "r1"
+ interface = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to ffaa::1-5 receivers")
+ result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("shut the direct link to R1 ")
+ intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ 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 ipv6 mroute'"
+ " 'show ipv6 pim state' "
+ )
+
+ source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0]
+ input_dict = [
+ {
+ "dut": "r5",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r5"]["links"]["r4"]["interface"],
+ },
+ {
+ "dut": "r5",
+ "src_address": source,
+ "iif": topo["routers"]["r5"]["links"]["r3"]["interface"],
+ "oil": topo["routers"]["r5"]["links"]["r4"]["interface"],
+ },
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify multicast traffic")
+ result = verify_sg_traffic(tgen, "r5", MLD_JOIN_RANGE_1, source, "ipv6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n mroutes traffic " "still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Stop multicast traffic from FRR3")
+ dut = "i2"
+ intf = topo["routers"]["i2"]["links"]["r3"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("(*,G) present on R5 after source shut")
+
+ input_dict_1 = [
+ {
+ "dut": "r5",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r5"]["links"]["r4"]["interface"],
+ },
+ ]
+ for data in input_dict_1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("stop mld receiver from FRR1")
+ dut = "i1"
+ intf = topo["routers"]["i1"]["links"]["r1"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ "After stopping receiver (*, G) and (S, G) also got removed from transit"
+ " router 'show ipv6 mroute'"
+ )
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n mroutes "
+ "not removed after removing the receivers \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):
+ """
+ Verify (S,G) should not create if RP is not reachable
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ app_helper.stop_all_hosts()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure RP on FRR2 (loopback interface) for " "the group range ffaa::1-5")
+
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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 mld on FRR1 interface and send mld join ffaa::1-5")
+
+ step("send mld join (ffaa::1-5) to R1")
+ result = app_helper.run_join("i1", MLD_JOIN_RANGE_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to ffaa::1-5 receivers")
+ result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure one MLD interface on FRR3 node and send MLD" " join (ffcc::1)")
+ r3_i8 = topo["routers"]["r3"]["links"]["i8"]["interface"]
+ input_dict = {"r3": {"mld": {"interfaces": {r3_i8: {"mld": {"version": "1"}}}}}}
+ result = create_mld_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("send mld join (ffaa::1-5) to R1")
+ result = app_helper.run_join("i8", MLD_JOIN_RANGE_1, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("verify MLD groups received ")
+ dut = "r3"
+ interface = topo["routers"]["r3"]["links"]["i8"]["interface"]
+ result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0]
+ input_dict = [
+ {
+ "dut": "r3",
+ "src_address": "*",
+ "iif": topo["routers"]["r3"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["r3"]["links"]["i8"]["interface"],
+ },
+ {
+ "dut": "r3",
+ "src_address": source,
+ "iif": topo["routers"]["r3"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r3"]["links"]["i8"]["interface"],
+ },
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut the RP connected interface from r3 ( r2 to r3) link")
+ dut = "r3"
+ intf = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("Clear the mroute on r3")
+ clear_pim6_mroute(tgen, "r3")
+
+ step(
+ "After Shut the RP interface and clear the mroute verify all "
+ "(*,G) and (S,G) got timeout from FRR3 node , verify using "
+ " 'show ipv6 mroute' "
+ )
+ r3_r2 = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ r3_i8 = topo["routers"]["r3"]["links"]["i8"]["interface"]
+
+ result = verify_mroutes(
+ tgen, "r3", "*", MLD_JOIN_RANGE_1, r3_r2, r3_i8, 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("mld groups are present verify using 'show ip mld group'")
+ dut = "r1"
+ interface = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_modify_mld_query_timer_p0(request):
+ """
+ Verify modification of mld query timer should get update
+ accordingly
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ app_helper.stop_all_hosts()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("send mld join (ffaa::1-5) to R1")
+ result = app_helper.run_join("i8", MLD_JOIN_RANGE_1, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Enable MLD on receiver interface")
+ intf_r3_i8 = topo["routers"]["r3"]["links"]["i8"]["interface"]
+ input_dict_1 = {
+ "r3": {"mld": {"interfaces": {intf_r3_i8: {"mld": {"version": "1"}}}}}
+ }
+
+ result = create_mld_config(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("verify MLD groups received ")
+ dut = "r3"
+ interface = topo["routers"]["r3"]["links"]["i8"]["interface"]
+ result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP on R2 (loopback interface) for the" " group range ffaa::1-5")
+
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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 ffaa::1-5 receivers")
+ result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ipv6 mroute' showing correct RPF and OIF"
+ " interface for (*,G) and (S,G) entries on all the nodes"
+ )
+
+ source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0]
+ input_dict_4 = [
+ {
+ "dut": "r3",
+ "src_address": source,
+ "iif": topo["routers"]["r3"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r3"]["links"]["i8"]["interface"],
+ },
+ {
+ "dut": "r3",
+ "src_address": "*",
+ "iif": topo["routers"]["r3"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["r3"]["links"]["i8"]["interface"],
+ },
+ ]
+ for data in input_dict_4:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ mwait=20,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ipv6 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"], MLD_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Modify mld query interval default to other timer on FRR1" ", 3 times")
+
+ input_dict_1 = {
+ "r3": {
+ "mld": {
+ "interfaces": {intf_r3_i8: {"mld": {"query": {"query-interval": 100}}}}
+ }
+ }
+ }
+ result = create_mld_config(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_mld_config(tgen, input_dict_1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_2 = {
+ "r3": {
+ "mld": {
+ "interfaces": {intf_r3_i8: {"mld": {"query": {"query-interval": 200}}}}
+ }
+ }
+ }
+ result = create_mld_config(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_mld_config(tgen, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r3": {
+ "mld": {
+ "interfaces": {intf_r3_i8: {"mld": {"query": {"query-interval": 300}}}}
+ }
+ }
+ }
+ result = create_mld_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_mld_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r3": {
+ "mld": {
+ "interfaces": {
+ intf_r3_i8: {
+ "mld": {"query": {"query-interval": 300, "delete": True}}
+ }
+ }
+ }
+ }
+ }
+ result = create_mld_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("veriffy mroutes after query modification")
+ for data in input_dict_4:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ mwait=20,
+ )
+ 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_mld_max_query_response_timer_p0(request):
+ """
+ Verify modification of mld max query response timer
+ should get update accordingly
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ app_helper.stop_all_hosts()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Enable mld on FRR1 interface and send MLD join")
+ step("send mld join (ffaa::1-5) to R1")
+ result = app_helper.run_join("i1", MLD_JOIN_RANGE_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ step("Configure mld query response time to 10 sec on FRR1")
+ input_dict_1 = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ r1_i1: {
+ "mld": {
+ "version": "1",
+ "query": {"query-max-response-time": 10},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_mld_config(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_mld_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": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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 ffaa::1-5 receivers")
+ result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ipv6 mroute' showing correct RPF and OIF"
+ " interface for (*,G) and (S,G) entries on all the nodes"
+ )
+
+ source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0]
+ input_dict_5 = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source,
+ "iif": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r3",
+ "src_address": source,
+ "iif": topo["routers"]["r3"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r3"]["links"]["r2"]["interface"],
+ },
+ ]
+ for data in input_dict_5:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ mwait=20,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ipv6 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"], MLD_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Delete the PIM and mld on FRR1")
+ r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_dict_1 = {"r1": {"pim6": {"disable": [r1_i1]}}}
+ result = create_pim_config(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_2 = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ r1_i1: {
+ "mld": {
+ "version": "1",
+ "delete": True,
+ "query": {"query-max-response-time": 10, "delete": True},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_mld_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 100sec on FRR1")
+ input_dict_3 = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ r1_i1: {
+ "mld": {
+ "version": "1",
+ "query": {"query-max-response-time": 100},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_mld_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_mld_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 mld and mld version 2 on FRR1"
+ " on FRR1"
+ )
+
+ input_dict_3 = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ r1_i1: {
+ "mld": {
+ "version": "1",
+ "query": {"query-max-response-time": 110},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_mld_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_mld_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ r1_i1: {
+ "mld": {
+ "version": "1",
+ "query": {"query-max-response-time": 120},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_mld_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_mld_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ r1_i1: {
+ "mld": {
+ "version": "1",
+ "query": {"query-max-response-time": 140},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_mld_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_mld_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ r1_i1: {
+ "mld": {
+ "version": "1",
+ "query": {"query-max-response-time": 150},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_mld_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_mld_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Enable mld and mld version 2 on FRR1 on FRR1")
+
+ input_dict_4 = {"r1": {"mld": {"interfaces": {r1_i1: {"mld": {"version": "1"}}}}}}
+ result = create_mld_config(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_mld_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_impact_on_multicast_traffic_when_RP_removed_p0(request):
+ """
+ Verify removing the RP should not impact the multicast
+ data traffic
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ app_helper.stop_all_hosts()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("send multicast traffic for group range ffaa::1-5")
+
+ step("Send multicast traffic from FRR3 to ffaa::1-5 receivers")
+ result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for group (ffaa::1) on r5")
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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 mld on FRR1 interface and send MLD join")
+ step("send mld join (ffaa::1-5) to R1")
+ result = app_helper.run_join("i1", MLD_JOIN_RANGE_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After SPT switchover traffic is flowing (FRR3-FRR2-FRR1)"
+ " and (s,g) OIL updated correctly using 'show ipv6 mroute'"
+ " 'show ipv6 pim upstream'"
+ )
+
+ source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0]
+ input_dict = [
+ {
+ "dut": "r3",
+ "src_address": source,
+ "iif": topo["routers"]["r3"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r3"]["links"]["r2"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source,
+ "iif": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ ]
+
+ for data in input_dict:
+ if data["dut"] == "r1":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ for data in input_dict:
+ if data["dut"] == "r3":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ 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"], MLD_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut and No shut RP interface in r5")
+ dut = "r5"
+ intf = "lo"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "After no shut of RP verify (*,G) entries re-populated again"
+ " and uptime go reset verify using 'show ipv6 mroute'"
+ " 'show ipv6 pim state'"
+ )
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Remove static RP for group (ffaa::1) on r5")
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].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 remove of RP verify no impact on (s,g)")
+
+ input_dict = [
+ {
+ "dut": "r3",
+ "src_address": source,
+ "iif": topo["routers"]["r3"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r3"]["links"]["r2"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source,
+ "iif": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ ]
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify multicast traffic is flowing")
+ result = verify_sg_traffic(tgen, "r1", MLD_JOIN_RANGE_1, source, "ipv6")
+ 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_pim6_sm_topo1/test_multicast_pim6_sm2.py b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py
new file mode 100644
index 0000000..8b174bf
--- /dev/null
+++ b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py
@@ -0,0 +1,591 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+Following tests are covered to test multicast pim6 sm:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+1. Verify (*,G) and (S,G) entry populated again after clear the
+PIM nbr and mroute from FRR node
+2. Verify SPT switchover working when RPT and SPT path is
+different
+"""
+
+import sys
+import time
+
+import pytest
+from lib.common_config import (
+ required_linux_kernel_version,
+ reset_config_on_routers,
+ shutdown_bringup_interface,
+ start_topology,
+ step,
+ write_test_footer,
+ write_test_header,
+)
+from lib.pim import (
+ McastTesterHelper,
+ clear_pim6_mroute,
+ create_pim_config,
+ verify_mroutes,
+ verify_pim_interface_traffic,
+ verify_sg_traffic,
+ verify_upstream_iif,
+)
+from lib.bgp import (
+ verify_bgp_convergence,
+)
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+
+# Global variables
+GROUP_RANGE = "ff00::/8"
+
+GROUP_RANGE_1 = [
+ "ffaa::1/128",
+ "ffaa::2/128",
+ "ffaa::3/128",
+ "ffaa::4/128",
+ "ffaa::5/128",
+]
+MLD_JOIN_RANGE_1 = ["ffaa::1", "ffaa::2", "ffaa::3", "ffaa::4", "ffaa::5"]
+
+GROUP_RANGE_2 = [
+ "ffbb::1/128",
+ "ffbb::2/128",
+ "ffbb::3/128",
+ "ffbb::4/128",
+ "ffbb::5/128",
+]
+MLD_JOIN_RANGE_2 = ["ffbb::1", "ffbb::2", "ffbb::3", "ffbb::4", "ffbb::5"]
+GROUP_RANGE_3 = [
+ "ffcc::1/128",
+ "ffcc::2/128",
+ "ffcc::3/128",
+ "ffcc::4/128",
+ "ffcc::5/128",
+]
+MLD_JOIN_RANGE_3 = ["ffcc::1", "ffcc::2", "ffcc::3", "ffcc::4", "ffcc::5"]
+
+HELLO_TIMER = 1
+HOLD_TIMER = 3
+PREFERRED_NEXT_HOP = "link_local"
+ASSERT_MSG = "Testcase {} : Failed Error: {}"
+
+pytestmark = [pytest.mark.pim6d]
+
+
+@pytest.fixture(scope="function")
+def app_helper():
+ # helper = McastTesterHelper(get_topogen())
+ # yield helepr
+ # helper.cleanup()
+ # Even better use contextmanager functionality:
+ with McastTesterHelper(get_topogen()) as ah:
+ yield ah
+
+
+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")
+
+ json_file = "multicast_pim6_sm_topo1.json"
+ 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 deamons and then start routers
+ start_topology(tgen)
+
+ # 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)
+
+ # Verify BGP convergence
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ 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 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_clear_mroute_and_verify_multicast_data_p0(request, app_helper):
+ """
+ Verify (*,G) and (S,G) entry populated again after clear the
+ PIM nbr and mroute from FRR node
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ app_helper.stop_all_hosts()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure static RP on r4 for group (ffcc::1-5)")
+ input_dict = {
+ "r4": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv6"].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 mld on FRR1 interface and send mld join ffaa::1 "
+ "to ffaa::5 from different interfaces"
+ )
+
+ step("send mld join (ffaa::1-5) to R1")
+ result = app_helper.run_join("i1", MLD_JOIN_RANGE_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to all the receivers" "ffaa::1-5")
+
+ result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Clear the mroute on r1, wait for 5 sec")
+ result = clear_pim6_mroute(tgen, "r1")
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After clear ip mroute (*,g) entries are re-populated again"
+ " with same OIL and IIF, verify using 'show ipv6 mroute' and "
+ " 'show ipv6 pim upstream' "
+ )
+
+ source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0]
+ input_dict = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ }
+ ]
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ipv6 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"], MLD_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ step("Clear the mroute on r3, wait for 5 sec")
+ result = clear_pim6_mroute(tgen, "r3")
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ipv6 mroute' showing correct RPF and OIF"
+ " interface for (*,G) and (S,G) entries on all the nodes"
+ )
+
+ input_dict = [
+ {
+ "dut": "r3",
+ "src_address": source,
+ "iif": topo["routers"]["r3"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r3"]["links"]["r2"]["interface"],
+ }
+ ]
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ipv6 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"], MLD_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ step("Clear the mroute on r2, wait for 5 sec")
+ result = clear_pim6_mroute(tgen, "r2")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ipv6 mroute' showing correct RPF and OIF"
+ " interface for (*,G) and (S,G) entries on all the nodes"
+ )
+
+ input_dict = [
+ {
+ "dut": "r2",
+ "src_address": source,
+ "iif": topo["routers"]["r2"]["links"]["r3"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["r1"]["interface"],
+ }
+ ]
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ipv6 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"], MLD_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Clear the mroute on r1, r3, wait for 5 sec")
+ result = clear_pim6_mroute(tgen, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = clear_pim6_mroute(tgen, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ipv6 mroute' showing correct RPF and OIF"
+ " interface for (*,G) and (S,G) entries on all the nodes"
+ )
+
+ input_dict = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r3",
+ "src_address": source,
+ "iif": topo["routers"]["r3"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r3"]["links"]["r2"]["interface"],
+ },
+ ]
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ MLD_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ipv6 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"], MLD_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "multicast traffic is resume for all the receivers using "
+ " 'show ip multicast' "
+ )
+ result = verify_sg_traffic(tgen, "r1", MLD_JOIN_RANGE_1, source, "ipv6")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_SPT_switchover_when_RPT_and_SPT_path_is_different_p0(
+ request, app_helper
+):
+ """
+ Verify SPT switchover working when RPT and SPT path is
+ different
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo, addr_type="ipv6")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure static RP for (ffcc::1-5) and " "(ffbb::1-5) in r5")
+
+ _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3
+ _MLD_JOIN_RANGE = MLD_JOIN_RANGE_2 + MLD_JOIN_RANGE_3
+
+ input_dict = {
+ "r5": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r5"]["links"]["lo"]["ipv6"].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 mld join (ffbb::1-5, ffcc::1-5) to R1")
+ result = app_helper.run_join("i1", _MLD_JOIN_RANGE, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("registerRx and registerStopTx value before traffic sent")
+ intf_r5 = topo["routers"]["r5"]["links"]["r3"]["interface"]
+ state_dict = {"r5": {intf_r5: ["registerRx", "registerStopTx"]}}
+ 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 multicast traffic from FRR3 to all the receivers" "ffbb::1-5 , ffcc::1-5"
+ )
+ result = app_helper.run_traffic("i2", _MLD_JOIN_RANGE, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify in FRR3 sending initial packet to RP using"
+ " 'show ipv6 mroute' and mroute OIL is towards RP."
+ )
+
+ source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0]
+
+ r3_i2 = topo["routers"]["r3"]["links"]["i2"]["interface"]
+ r3_r5 = topo["routers"]["r3"]["links"]["r5"]["interface"]
+ r3_r2 = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+
+ result = verify_mroutes(tgen, "r3", source, _MLD_JOIN_RANGE, r3_i2, [r3_r5, r3_r2])
+ assert result is True, "Testcase {} : " "Failed Error: {}".format(tc_name, result)
+
+ result = verify_mroutes(tgen, "r3", source, _MLD_JOIN_RANGE, r3_i2, r3_r2)
+ 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 mroute' and 'show pim upstream'"
+ )
+
+ input_dict = [
+ {"dut": "r3", "src_address": source, "iif": r3_i2, "oil": r3_r2},
+ {"dut": "r1", "src_address": source, "iif": r1_r2, "oil": r1_i1},
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _MLD_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"], _MLD_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Stop the traffic to all the receivers")
+ dut = "i2"
+ intf = topo["routers"]["i2"]["links"]["r3"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ "Null register packet being send periodically from FRR3 to RP, "
+ "verify using show ipv6 mroute on RP, have (S, G) entries null OIL"
+ " 'show ipv6 mroute' and verify show ip pim interface traffic"
+ "(In RP Register msg should be received and Register stop should"
+ " be transmitted)"
+ )
+
+ result = verify_upstream_iif(
+ tgen, "r3", "Unknown", source, _MLD_JOIN_RANGE, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("registerRx and registerStopTx value after traffic sent")
+ 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_pim6_static_rp_topo1/__init__.py b/tests/topotests/multicast_pim6_static_rp_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/multicast_pim6_static_rp_topo1/__init__.py
diff --git a/tests/topotests/multicast_pim6_static_rp_topo1/multicast_pim6_static_rp.json b/tests/topotests/multicast_pim6_static_rp_topo1/multicast_pim6_static_rp.json
new file mode 100644
index 0000000..9edfae4
--- /dev/null
+++ b/tests/topotests/multicast_pim6_static_rp_topo1/multicast_pim6_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_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py
new file mode 100755
index 0000000..2332633
--- /dev/null
+++ b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py
@@ -0,0 +1,1263 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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
+
+1. Verify upstream interfaces(IIF) and join state are updated
+ properly after adding and deleting the static RP
+2. Verify IIF and OIL in "show ipv6 PIM6 state" updated properly when
+ RP becomes unreachable
+3. Verify RP becomes reachable after MLD join received, PIM6 join
+ towards RP is sent immediately
+4. Verify (*,G) and (S,G) populated correctly when SPT and RPT
+ share the same path
+5. Verify OIF and RPF for (*,G) and (S,G) when static RP configure
+ in LHR router
+6. Verify OIF and RFP for (*,G) and (S,G) when static RP configure
+ in FHR router
+7. Verify (*,G) and (S,G) populated correctly when RPT and SPT path
+ are different
+8. Verify PIM6 join send towards the higher preferred RP
+9. Verify PIM6 prune send towards the lower preferred RP
+"""
+import sys
+import time
+
+import pytest
+from lib.common_config import (
+ check_router_status,
+ reset_config_on_routers,
+ shutdown_bringup_interface,
+ start_topology,
+ step,
+ write_test_footer,
+ write_test_header,
+)
+from lib.pim import (
+ McastTesterHelper,
+ clear_pim6_interface_traffic,
+ create_pim_config,
+ get_pim6_interface_traffic,
+ verify_join_state_and_timer,
+ verify_mld_groups,
+ verify_mroutes,
+ verify_pim6_neighbors,
+ verify_pim_interface_traffic,
+ verify_pim_rp_info,
+ verify_pim_state,
+ verify_upstream_iif,
+)
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json, build_topo_from_json
+from lib.topolog import logger
+
+# Global variables
+GROUP_RANGE_1 = "ff08::/64"
+GROUP_ADDRESS_1 = "ff08::1"
+GROUP_RANGE_3 = "ffaa::/64"
+GROUP_ADDRESS_3 = "ffaa::1"
+GROUP_RANGE_4 = "ff00::/8"
+GROUP_ADDRESS_4 = "ff00::1"
+STAR = "*"
+SOURCE = "Static"
+ASSERT_MSG = "Testcase {} : Failed Error: {}"
+
+pytestmark = [pytest.mark.pim6d]
+
+
+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_pim6_static_rp.json"
+ 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)
+
+ # 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 PIM6 neighbors
+ result = verify_pim6_neighbors(tgen, TOPO)
+ assert result is True, "setup_module :Failed \n Error:" " {}".format(result)
+
+ 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)
+
+
+#####################################################
+#
+# Local API
+#
+#####################################################
+
+
+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_pim6_add_delete_static_RP_p0(request):
+ """
+ Verify upstream interfaces(IIF) and join state are updated
+ properly after adding and deleting the static RP
+ Verify IIF and OIL in "show ipv6 PIM6 state" updated properly when
+ RP becomes unreachable
+ Verify RP becomes reachable after MLD join received, PIM6 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():
+ check_router_status(tgen)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+
+ app_helper.stop_all_hosts()
+
+ step("Shut link b/w R1 and R3 and R1 and R4 as per testcase 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 PIM6 between r1 and r2")
+ step(
+ "Enable MLD on r1 interface and send MLD " "join {} to r1".format(GROUP_RANGE_1)
+ )
+ 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_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify show ipv6 mld group without any MLD join")
+ dut = "r1"
+ intf_r1_r0 = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mld_groups(tgen, dut, intf_r1_r0, GROUP_ADDRESS_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: mld group present without any MLD join \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("Verify show ipv6 PIM6 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 {} to R1".format(GROUP_ADDRESS_1))
+ result = app_helper.run_join("r0", GROUP_ADDRESS_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify MLD groups")
+ result = verify_mld_groups(tgen, dut, intf_r1_r0, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_1, oif, rp_address, SOURCE)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, oif, STAR, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, oif, STAR, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify PIM6 state")
+ result = verify_pim_state(tgen, dut, oif, iif, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_1, oif, iif)
+ assert result is True, ASSERT_MSG.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_1,
+ "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_1, 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, GROUP_ADDRESS_1, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "Upstream ({}, {}) is still in join state \n Error: {}".format(
+ tc_name, STAR, GROUP_ADDRESS_1, result
+ )
+ )
+
+ step("r1: Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, oif, STAR, GROUP_ADDRESS_1, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "Upstream ({}, {}) timer is still running \n Error: {}".format(
+ tc_name, STAR, GROUP_ADDRESS_1, result
+ )
+ )
+
+ step("r1: Verify PIM6 state")
+ result = verify_pim_state(tgen, dut, oif, iif, GROUP_ADDRESS_1, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "PIM state for group: {} is still Active \n Error: {}".format(
+ tc_name, GROUP_ADDRESS_1, result
+ )
+ )
+
+ step("r1: Verify ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_1, oif, iif, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "mroute ({}, {}) is still present \n Error: {}".format(
+ tc_name, STAR, GROUP_ADDRESS_1, result
+ )
+ )
+
+ step("r1: Verify show ipv6 PIM6 interface traffic without any MLD 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)
+
+
+def test_pim6_SPT_RPT_path_same_p1(request):
+ """
+ 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()
+
+ step("Shut link b/w R1->R3, R1->R4 and R3->R1, R3->R4 as per " "testcase topology")
+ intf_r1_r3 = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
+ intf_r1_r4 = TOPO["routers"]["r1"]["links"]["r4"]["interface"]
+ intf_r3_r1 = TOPO["routers"]["r3"]["links"]["r1"]["interface"]
+ intf_r3_r4 = TOPO["routers"]["r3"]["links"]["r4"]["interface"]
+ for intf in [intf_r1_r3, intf_r1_r4]:
+ shutdown_bringup_interface(tgen, "r1", intf, ifaceaction=False)
+
+ for intf in [intf_r3_r1, intf_r3_r4]:
+ shutdown_bringup_interface(tgen, "r3", intf, ifaceaction=False)
+
+ step("Enable the PIM6 on all the interfaces of r1, r2, r3 and r4 routers")
+ step(
+ "Configure RP on r2 (loopback interface) for the group range {}".format(
+ GROUP_ADDRESS_1
+ )
+ )
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].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 MLD on r1 interface and send MLD join {} to R1".format(GROUP_ADDRESS_1)
+ )
+ result = app_helper.run_join("r0", GROUP_ADDRESS_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify MLD groups")
+ dut = "r1"
+ intf_r1_r0 = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mld_groups(tgen, dut, intf_r1_r0, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("Send multicast traffic from R5")
+ SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0]
+ result = app_helper.run_traffic("r5", GROUP_ADDRESS_1, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r2: Verify RP info")
+ dut = "r2"
+ oif = "lo"
+ rp_address = TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
+ result = verify_pim_rp_info(tgen, TOPO, dut, GROUP_RANGE_1, oif, rp_address, SOURCE)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_1
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.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_1)
+ assert result is True, ASSERT_MSG.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_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r2"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream IIF interface")
+ iif = TOPO["routers"]["r2"]["links"]["r3"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_1
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r2: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = TOPO["routers"]["r3"]["links"]["r5"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_1, 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 = TOPO["routers"]["r3"]["links"]["r2"]["interface"]
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_pim6_RP_configured_as_LHR_p1(request):
+ """
+ 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()
+
+ step("Enable MLD on r1 interface")
+ step("Enable the PIM6 on all the interfaces of r1, r2, r3 and r4 routers")
+
+ step("r1: Configure r1(LHR) as RP")
+ input_dict = {
+ "r1": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r1"]["links"]["lo"]["ipv6"].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("r1: Shut not Shut loopback interface")
+ shutdown_bringup_interface(tgen, "r1", "lo", False)
+ shutdown_bringup_interface(tgen, "r1", "lo", True)
+
+ step("r1: Verify RP info")
+ dut = "r1"
+ iif = "lo"
+ oif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ rp_address = TOPO["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0]
+ result = verify_pim_rp_info(tgen, TOPO, dut, GROUP_RANGE_1, iif, rp_address, SOURCE)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("send mld join {} to R1".format(GROUP_ADDRESS_1))
+ result = app_helper.run_join("r0", GROUP_ADDRESS_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify MLD groups")
+ dut = "r1"
+ intf_r1_r0 = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mld_groups(tgen, dut, intf_r1_r0, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r5: Send multicast traffic for group {}".format(GROUP_ADDRESS_1))
+ SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0]
+ result = app_helper.run_traffic("r5", GROUP_ADDRESS_1, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
+ SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_1
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = TOPO["routers"]["r3"]["links"]["r5"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_1, 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 = TOPO["routers"]["r3"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_pim6_RP_configured_as_FHR_p1(request):
+ """
+ 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()
+
+ step("Enable MLD on r1 interface")
+ step("Enable the PIM6 on all the interfaces of r1, r2, r3 and r4 routers")
+ step("r3: Configure r3(FHR) as RP")
+ input_dict = {
+ "r3": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r3"]["links"]["lo"]["ipv6"].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("r1: Verify RP info")
+ dut = "r1"
+ iif = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
+ oif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ rp_address = TOPO["routers"]["r3"]["links"]["lo"]["ipv6"].split("/")[0]
+ result = verify_pim_rp_info(tgen, TOPO, dut, GROUP_RANGE_1, iif, rp_address, SOURCE)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("send mld join {} to R1".format(GROUP_ADDRESS_1))
+ result = app_helper.run_join("r0", GROUP_ADDRESS_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify MLD groups")
+ dut = "r1"
+ intf_r1_r0 = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mld_groups(tgen, dut, intf_r1_r0, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r5: Send multicast traffic for group {}".format(GROUP_ADDRESS_1))
+ SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0]
+ result = app_helper.run_traffic("r5", GROUP_ADDRESS_1, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_1
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = TOPO["routers"]["r3"]["links"]["r5"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_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 = TOPO["routers"]["r3"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_pim6_SPT_RPT_path_different_p1(request):
+ """
+ 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()
+
+ step("Enable MLD on r1 interface")
+ step("Enable the PIM6 on all the interfaces of r1, r2, r3 and r4 routers")
+ step("r2: Configure r2 as RP")
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].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("r2: Verify RP info")
+ dut = "r2"
+ iif = "lo"
+ rp_address = TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_ADDRESS_1, iif, rp_address, SOURCE
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("send mld join {} to R1".format(GROUP_ADDRESS_1))
+ result = app_helper.run_join("r0", GROUP_ADDRESS_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify MLD groups")
+ dut = "r1"
+ intf_r1_r0 = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mld_groups(tgen, dut, intf_r1_r0, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r5: Send multicast traffic for group {}".format(GROUP_ADDRESS_1))
+ SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0]
+ result = app_helper.run_traffic("r5", GROUP_ADDRESS_1, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ oif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ iif = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
+ SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0]
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_1
+ )
+ assert result is True, ASSERT_MSG.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_1)
+ assert result is True, ASSERT_MSG.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_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r2"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = TOPO["routers"]["r3"]["links"]["r5"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.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_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 = TOPO["routers"]["r3"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream IIF interface")
+ dut = "r2"
+ iif = TOPO["routers"]["r2"]["links"]["r3"]["interface"]
+ result = verify_upstream_iif(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_1, joinState="NotJoined"
+ )
+ assert result is True, ASSERT_MSG.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_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_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_pim6_send_join_on_higher_preffered_rp_p1(request):
+ """
+ Verify PIM6 join send towards the higher preferred RP
+ Verify PIM6 prune send towards the lower preferred RP
+
+ 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()
+
+ step("Enable MLD on r1 interface")
+ step("Enable the PIM66 on all the interfaces of r1, r2, r3 and r4 routers")
+ step(
+ "Configure RP on r2 (loopback interface) for the group range {}".format(
+ GROUP_RANGE_4
+ )
+ )
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_4,
+ }
+ ]
+ }
+ }
+ }
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("r3 : Make all interface not reachable")
+ intf_r3_r1 = TOPO["routers"]["r3"]["links"]["r1"]["interface"]
+ intf_r3_r2 = TOPO["routers"]["r3"]["links"]["r2"]["interface"]
+ intf_r3_r4 = TOPO["routers"]["r3"]["links"]["r4"]["interface"]
+ intf_r1_r3 = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
+ intf_r2_r3 = TOPO["routers"]["r2"]["links"]["r3"]["interface"]
+ intf_r4_r3 = TOPO["routers"]["r4"]["links"]["r3"]["interface"]
+
+ for dut, intf in zip(["r1", "r2", "r3"], [intf_r1_r3, intf_r2_r3, intf_r4_r3]):
+ shutdown_bringup_interface(tgen, dut, intf, ifaceaction=False)
+
+ for intf in [intf_r3_r1, intf_r3_r4, intf_r3_r4]:
+ shutdown_bringup_interface(tgen, "r3", intf, ifaceaction=False)
+
+ step("Verify show ipv6 PIM6 interface traffic without any mld join")
+ state_dict = {"r1": {TOPO["routers"]["r1"]["links"]["r4"]["interface"]: ["joinTx"]}}
+
+ state_before = get_pim6_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 mld join {} to R1".format(GROUP_ADDRESS_3))
+ result = app_helper.run_join("r0", GROUP_ADDRESS_3, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify MLD groups")
+ dut = "r1"
+ intf_r1_r0 = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mld_groups(tgen, dut, intf_r1_r0, GROUP_ADDRESS_3)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("Configure RP on r4 (loopback interface) for the group range " "ffaa::/128")
+ input_dict = {
+ "r4": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r4"]["links"]["lo"]["ipv6"].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("r1 : Verify RP info for group {}".format(GROUP_ADDRESS_4))
+ dut = "r1"
+ iif = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ rp_address_1 = TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_ADDRESS_4, iif, rp_address_1, SOURCE
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1 : Verify RP info for group {}".format(GROUP_ADDRESS_3))
+ dut = "r1"
+ iif = TOPO["routers"]["r1"]["links"]["r4"]["interface"]
+ rp_address_2 = TOPO["routers"]["r4"]["links"]["lo"]["ipv6"].split("/")[0]
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_ADDRESS_3, iif, rp_address_2, SOURCE
+ )
+ assert result is True, ASSERT_MSG.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_pim6_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")
+ oif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_3, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1 : Verify PIM6 state")
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS_3)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1 : Verify upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_3)
+ assert result is True, ASSERT_MSG.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_3)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ clear_pim6_interface_traffic(tgen, TOPO)
+
+ step("r1 : Verify joinTx, pruneTx count before RP gets deleted")
+ state_dict = {
+ "r1": {
+ TOPO["routers"]["r1"]["links"]["r2"]["interface"]: ["joinTx"],
+ TOPO["routers"]["r1"]["links"]["r4"]["interface"]: ["pruneTx"],
+ }
+ }
+ state_before = get_pim6_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 {}".format(GROUP_RANGE_3))
+ input_dict = {
+ "r4": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r4"]["links"]["lo"]["ipv6"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_3,
+ "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 for group {}".format(GROUP_RANGE_3))
+ iif = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_3, iif, rp_address_1, SOURCE
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1 : Verify rp-info for group {}".format(GROUP_RANGE_4))
+ iif = TOPO["routers"]["r1"]["links"]["r4"]["interface"]
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_4, oif, rp_address_2, SOURCE, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: rp-info is present for group {} \n Error: {}".format(
+ tc_name, GROUP_RANGE_4, result
+ )
+ )
+
+ step(
+ "r1 : Verify RPF interface updated in mroute when higher preferred"
+ "RP gets deleted"
+ )
+ iif = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_3, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+ logger.info("Expected behavior: %s", result)
+
+ step(
+ "r1 : Verify IIF and OIL in show ipv6 PIM6 state updated when higher"
+ "preferred overlapping RP is deleted"
+ )
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS_3)
+ assert result is True, ASSERT_MSG.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_3)
+ assert result is True, ASSERT_MSG.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_3, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.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_pim6_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)
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py
new file mode 100755
index 0000000..39497e9
--- /dev/null
+++ b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py
@@ -0,0 +1,1287 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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
+
+1. Configure multiple groups (10 grps) with same RP address
+2. Verify IIF and OIL in updated in mroute when upstream interface
+ configure as RP
+3. Verify RP info and (*,G) mroute after deleting the RP and shut /
+ no shut the RPF interface.
+"""
+
+import os
+import sys
+import time
+
+import pytest
+from lib.common_config import (
+ create_debug_log_config,
+ reset_config_on_routers,
+ shutdown_bringup_interface,
+ start_topology,
+ step,
+ write_test_footer,
+ write_test_header,
+)
+from lib.pim import (
+ McastTesterHelper,
+ create_pim_config,
+ verify_join_state_and_timer,
+ verify_mld_groups,
+ verify_mroutes,
+ verify_pim6_neighbors,
+ verify_pim_rp_info,
+ verify_upstream_iif,
+)
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json, build_topo_from_json
+from lib.topolog import logger
+
+# Global variables
+GROUP_RANGE_1 = "ff08::/64"
+GROUP_ADDRESS_1 = "ff08::1"
+GROUP_RANGE_3 = "ffaa::/64"
+GROUP_ADDRESS_3 = "ffaa::1"
+GROUP_RANGE_LIST_1 = [
+ "ffaa::1/128",
+ "ffaa::2/128",
+ "ffaa::3/128",
+ "ffaa::4/128",
+ "ffaa::5/128",
+]
+GROUP_RANGE_LIST_2 = [
+ "ffaa::6/128",
+ "ffaa::7/128",
+ "ffaa::8/128",
+ "ffaa::9/128",
+ "ffaa::10/128",
+]
+GROUP_ADDRESS_LIST_1 = ["ffaa::1", "ffaa::2", "ffaa::3", "ffaa::4", "ffaa::5"]
+GROUP_ADDRESS_LIST_2 = ["ffaa::6", "ffaa::7", "ffaa::8", "ffaa::9", "ffaa::10"]
+STAR = "*"
+SOURCE = "Static"
+ASSERT_MSG = "Testcase {} : Failed Error: {}"
+
+pytestmark = [pytest.mark.pim6d]
+
+
+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_pim6_static_rp.json"
+ 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)
+
+ # 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 PIM6 neighbors
+ result = verify_pim6_neighbors(tgen, TOPO)
+ assert result is True, "setup_module :Failed \n Error:" " {}".format(result)
+
+ 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)
+
+
+#####################################################
+#
+# Local API
+#
+#####################################################
+
+
+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_pim6_multiple_groups_same_RP_address_p2(request):
+ """
+ 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()
+
+ input_dict = {
+ "r1": {"debug": {"log_file": "r1_debug.log", "enable": ["pim6d"]}},
+ "r2": {"debug": {"log_file": "r2_debug.log", "enable": ["pim6d"]}},
+ "r3": {"debug": {"log_file": "r3_debug.log", "enable": ["pim6d"]}},
+ "r4": {"debug": {"log_file": "r4_debug.log", "enable": ["pim6d"]}},
+ }
+
+ result = create_debug_log_config(tgen, input_dict)
+
+ step("Enable MLD on r1 interface")
+ step("Enable the PIM6 on all the interfaces of r1, r2, r3 and r4 routers")
+ step("r2: Configure r2 as RP")
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].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("r2: verify rp-info")
+ dut = "r2"
+ oif = "lo"
+ rp_address = TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
+ result = verify_pim_rp_info(tgen, TOPO, dut, GROUP_RANGE_3, oif, rp_address, SOURCE)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ group_address_list = GROUP_ADDRESS_LIST_1 + GROUP_ADDRESS_LIST_2
+ step("r0: Send MLD join for 10 groups")
+ intf = TOPO["routers"]["r0"]["links"]["r1"]["interface"]
+ result = app_helper.run_join("r0", group_address_list, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify MLD groups")
+ dut = "r1"
+ intf_r1_r0 = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mld_groups(tgen, dut, intf_r1_r0, group_address_list)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r5: Send multicast traffic for group {}".format(group_address_list))
+ SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0]
+ result = app_helper.run_traffic("r5", group_address_list, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, STAR, group_address_list)
+ assert result is True, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, group_address_list, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ iif = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, group_address_list, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, group_address_list)
+ assert result is True, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.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, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r2"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, group_address_list, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = TOPO["routers"]["r3"]["links"]["r5"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, group_address_list)
+ assert result is True, ASSERT_MSG.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,
+ addr_type="ipv6",
+ 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 = TOPO["routers"]["r3"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, group_address_list, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream IIF interface")
+ dut = "r2"
+ iif = TOPO["routers"]["r2"]["links"]["r3"]["interface"]
+ result = verify_upstream_iif(
+ tgen, dut, iif, SOURCE_ADDRESS, group_address_list, joinState="NotJoined"
+ )
+ assert result is True, ASSERT_MSG.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,
+ addr_type="ipv6",
+ 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, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Delete RP configuration")
+ input_dict = {
+ "r1": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_3,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("r1: Shut the interface r1-r2-eth1 from R1 to R2")
+ dut = "r1"
+ intf = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: No Shut the interface r1-r2-eth1 from R1 to R2")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1: Configure RP")
+ input_dict = {
+ "r1": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].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("r1: Shut the interface r1-r0-eth0 from R1 to R2")
+ intf = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: No Shut the interface r1-r0-eth0 from R1 to R2")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, STAR, group_address_list)
+ assert result is True, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, group_address_list, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, group_address_list)
+ assert result is True, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.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, ASSERT_MSG.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, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r2"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, group_address_list, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = TOPO["routers"]["r3"]["links"]["r5"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, group_address_list)
+ assert result is True, ASSERT_MSG.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,
+ addr_type="ipv6",
+ 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 = TOPO["routers"]["r3"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, group_address_list, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_pim6_multiple_groups_different_RP_address_p2(request):
+ """
+ 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()
+
+ step("Enable MLD on r1 interface")
+ step("Enable the PIM6 on all the interfaces of r1, r2, r3 and r4 routers")
+ step("r2: Configure r2 as RP")
+ step("r4: Configure r4 as RP")
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_LIST_1,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r4"]["links"]["lo"]["ipv6"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_LIST_2,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("r2: Verify RP info")
+ dut = "r2"
+ oif = "lo"
+ rp_address = TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_LIST_1, oif, rp_address, SOURCE
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r4: Verify RP info")
+ dut = "r4"
+ rp_address = TOPO["routers"]["r4"]["links"]["lo"]["ipv6"].split("/")[0]
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_LIST_2, oif, rp_address, SOURCE
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ group_address_list = GROUP_ADDRESS_LIST_1 + GROUP_ADDRESS_LIST_2
+ step("r0: Send MLD join for 10 groups")
+ result = app_helper.run_join("r0", group_address_list, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify MLD groups")
+ dut = "r1"
+ intf_r1_r0 = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mld_groups(tgen, dut, intf_r1_r0, group_address_list)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r5: Send multicast traffic for group {}".format(group_address_list))
+ SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0]
+ result = app_helper.run_traffic("r5", group_address_list, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif1 = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ iif2 = TOPO["routers"]["r1"]["links"]["r4"]["interface"]
+
+ for _iif, _group in zip([iif1, iif2], [GROUP_ADDRESS_LIST_1, GROUP_ADDRESS_LIST_2]):
+ result = verify_upstream_iif(tgen, dut, _iif, STAR, _group)
+ assert result is True, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ iif = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ oif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1)
+ assert result is True, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.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, ASSERT_MSG.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, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r2"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream IIF interface")
+ iif = TOPO["routers"]["r2"]["links"]["r3"]["interface"]
+ result = verify_upstream_iif(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, joinState="NotJoined"
+ )
+ assert result is True, ASSERT_MSG.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,
+ addr_type="ipv6",
+ 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, ASSERT_MSG.format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = TOPO["routers"]["r3"]["links"]["r5"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1)
+ assert result is True, ASSERT_MSG.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,
+ addr_type="ipv6",
+ 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 = TOPO["routers"]["r3"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = TOPO["routers"]["r1"]["links"]["r4"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2)
+ assert result is True, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2)
+ assert result is True, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.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, ASSERT_MSG.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, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r4: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r4"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r4: Verify (S, G) upstream IIF interface")
+ iif = TOPO["routers"]["r4"]["links"]["r3"]["interface"]
+ result = verify_upstream_iif(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, joinState="NotJoined"
+ )
+ assert result is True, ASSERT_MSG.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,
+ addr_type="ipv6",
+ 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, ASSERT_MSG.format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = TOPO["routers"]["r3"]["links"]["r5"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2)
+ assert result is True, ASSERT_MSG.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,
+ addr_type="ipv6",
+ expected=False,
+ )
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = TOPO["routers"]["r3"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("Delete RP configuration")
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_LIST_1,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r4"]["links"]["lo"]["ipv6"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_LIST_2,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("r1, r2, r3, r4: Re-configure RP")
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_LIST_1,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r4"]["links"]["lo"]["ipv6"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_LIST_2,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("r1: Shut/No Shut the interfacesfrom R1 to R2, R4 and R0")
+ dut = "r1"
+ intf1 = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ intf2 = TOPO["routers"]["r1"]["links"]["r4"]["interface"]
+ intf3 = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ for intf in [intf1, intf2, intf3]:
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1)
+ assert result is True, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1)
+ assert result is True, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.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, ASSERT_MSG.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, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r2"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream IIF interface")
+ iif = TOPO["routers"]["r2"]["links"]["r3"]["interface"]
+ result = verify_upstream_iif(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, joinState="NotJoined"
+ )
+ assert result is True, ASSERT_MSG.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,
+ addr_type="ipv6",
+ 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, ASSERT_MSG.format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = TOPO["routers"]["r3"]["links"]["r5"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1)
+ assert result is True, ASSERT_MSG.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,
+ addr_type="ipv6",
+ 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 = TOPO["routers"]["r3"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = TOPO["routers"]["r1"]["links"]["r4"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2)
+ assert result is True, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2)
+ assert result is True, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.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, ASSERT_MSG.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, ASSERT_MSG.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, addr_type="ipv6"
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r4: Verify (*, G) ip mroutes")
+ oif = TOPO["routers"]["r4"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r4: Verify (S, G) upstream IIF interface")
+ iif = TOPO["routers"]["r4"]["links"]["r3"]["interface"]
+ result = verify_upstream_iif(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, joinState="NotJoined"
+ )
+ assert result is True, ASSERT_MSG.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,
+ addr_type="ipv6",
+ 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, ASSERT_MSG.format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = TOPO["routers"]["r3"]["links"]["r5"]["interface"]
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2)
+ assert result is True, ASSERT_MSG.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,
+ addr_type="ipv6",
+ 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 = TOPO["routers"]["r3"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_pim6_delete_RP_shut_noshut_upstream_interface_p1(request):
+ """
+ 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()
+
+ step("Enable MLD on r1 interface")
+ step("Enable the PIM6 on all the interfaces of r1, r2, r3 and r4 routers")
+ step("r2: Configure r2 as RP")
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].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("r2: verify rp-info")
+ dut = "r2"
+ oif = "lo"
+ rp_address = TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_ADDRESS_1, oif, rp_address, SOURCE
+ )
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r0: Send MLD join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify MLD groups")
+ dut = "r1"
+ intf_r1_r0 = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mld_groups(tgen, dut, intf_r1_r0, GROUP_ADDRESS_1)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes created")
+ iif = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ oif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes created")
+ dut = "r2"
+ iif = "lo"
+ oif = TOPO["routers"]["r2"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_1, iif, oif)
+ assert result is True, ASSERT_MSG.format(tc_name, result)
+
+ step("r1: Delete RP configuration")
+ input_dict = {
+ "r1": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].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("r1: Shut/No Shut the interface r1-r2-eth1/r1-r0-eth0 from R1 to R2")
+ dut = "r1"
+ intf1 = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ intf2 = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ for intf in [intf1, intf2]:
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ 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/r1-r3-eth2 towards RP")
+ intf3 = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
+ for intf in [intf1, intf3]:
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: Verify (*, G) ip mroutes cleared")
+ dut = "r1"
+ iif = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ oif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_1, 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 = TOPO["routers"]["r2"]["links"]["r1"]["interface"]
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_1, 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)
+
+
+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..9ee4112
--- /dev/null
+++ b/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py
@@ -0,0 +1,1767 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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,
+ 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 version should be >= 4.15")
+
+ 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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # 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 "
+ "Expected: Routes should not be present in {} RIB \n "
+ "Found: {}".format(tc_name, "f1", 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 "
+ "Expected: b1 should be chosen as BSR in {} \n "
+ "Found: {}".format(tc_name, "l1", 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 "
+ "Expected: Routes should not be present in {} RIB \n "
+ "Found: {}".format(tc_name, "f1", 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 "
+ "Expected: [{}]: BSR data should not be present after no-forward bsm \n "
+ "Found: {}".format(tc_name, "l1", 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 "
+ "Expected: [{}]: mroute (S, G) should be cleared from mroute table\n "
+ "Found: {}".format(tc_name, "i1", 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 "
+ "Expected: [{}]: b1 should not be chosen as BSR \n "
+ "Found: {}".format(tc_name, "i1", 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 "
+ "Expected: [{}]: mroute (S, G) should not be installed as RP is not available\n "
+ "Found: {}".format(tc_name, "i1", 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 "
+ "Expected: [{}]: bsr should be aged out \n "
+ "Found: {}".format(tc_name, "f1", 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 "
+ "Expected: [{}]: Upstream Join State timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (S, G) should not be installed \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: Routes should not be present in {} BGP RIB \n "
+ "Found: {}".format(tc_name, "l1", 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 "
+ "Expected: [{}]: mroute (S, G) should not be installed \n "
+ "Found: {}".format(tc_name, dut, 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..302a778
--- /dev/null
+++ b/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py
@@ -0,0 +1,1102 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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,
+ 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 version should be >= 4.15")
+
+ 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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # 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 "
+ "Expected: [{}]: mroute (S, G) should not be installed \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: b2 should not be chosen as bsr \n "
+ "Found: {}".format(tc_name, "f1", 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..00d38fe
--- /dev/null
+++ b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py
@@ -0,0 +1,1125 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # 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..7c509cd
--- /dev/null
+++ b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_static_routes_topo1.py
@@ -0,0 +1,924 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # 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..1987001
--- /dev/null
+++ b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py
@@ -0,0 +1,813 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # 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..1de6eac
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py
@@ -0,0 +1,1589 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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 version should be >= 4.19")
+
+ 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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: mroute (*, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, "c1", 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 "
+ "Expected: [{}]: mroute (*, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, "f1", 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..17f52cb
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py
@@ -0,0 +1,1825 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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 version should be >= 4.19")
+
+ 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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 \n "
+ "Expected: [{}]: Upstream IIF should be unknown \n "
+ "Found: {}".format(tc_name, "l1", result)
+ )
+
+ 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 \n "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], result)
+ )
+
+ 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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/igmp_group_all_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json
new file mode 100644
index 0000000..715aa1d
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json
@@ -0,0 +1 @@
+{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json
new file mode 100644
index 0000000..3bbcce1
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json
@@ -0,0 +1,51 @@
+{
+ "totalGroups":5,
+ "watermarkLimit":0,
+ "l1-i1-eth1":{
+ "name":"l1-i1-eth1",
+ "state":"up",
+ "address":"10.0.8.2",
+ "index":"*",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "groups":[
+ {
+ "group":"225.1.1.1",
+ "timer":"*",
+ "sourcesCount":1,
+ "version":2,
+ "uptime":"*"
+ },
+ {
+ "group":"225.1.1.2",
+ "timer":"*",
+ "sourcesCount":1,
+ "version":2,
+ "uptime":"*"
+ },
+ {
+ "group":"225.1.1.3",
+ "timer":"*",
+ "sourcesCount":1,
+ "version":2,
+ "uptime":"*"
+ },
+ {
+ "group":"225.1.1.4",
+ "timer":"*",
+ "sourcesCount":1,
+ "version":2,
+ "uptime":"*"
+ },
+ {
+ "group":"225.1.1.5",
+ "timer":"*",
+ "sourcesCount":1,
+ "version":2,
+ "uptime":"*"
+ }
+ ]
+ }
+}
+
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json
new file mode 100644
index 0000000..876befa
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json
@@ -0,0 +1 @@
+{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json
new file mode 100644
index 0000000..a3fb496
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json
@@ -0,0 +1,22 @@
+{
+ "totalGroups":5,
+ "watermarkLimit":0,
+ "l1-i1-eth1":{
+ "name":"l1-i1-eth1",
+ "state":"up",
+ "address":"10.0.8.2",
+ "index":"*",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "groups":[
+ {
+ "group":"225.1.1.5",
+ "timer":"*",
+ "sourcesCount":1,
+ "version":2,
+ "uptime":"*"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json
new file mode 100644
index 0000000..11ac5a0
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json
@@ -0,0 +1 @@
+{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json
new file mode 100644
index 0000000..10ae1af
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json
@@ -0,0 +1,61 @@
+{
+ "l1-i1-eth1":{
+ "name":"l1-i1-eth1",
+ "225.1.1.1":{
+ "group":"225.1.1.1",
+ "sources":[
+ {
+ "source":"*",
+ "timer":"*",
+ "forwarded":true,
+ "uptime":"*"
+ }
+ ]
+ },
+ "225.1.1.2":{
+ "group":"225.1.1.2",
+ "sources":[
+ {
+ "source":"*",
+ "timer":"*",
+ "forwarded":true,
+ "uptime":"*"
+ }
+ ]
+ },
+ "225.1.1.3":{
+ "group":"225.1.1.3",
+ "sources":[
+ {
+ "source":"*",
+ "timer":"*",
+ "forwarded":true,
+ "uptime":"*"
+ }
+ ]
+ },
+ "225.1.1.4":{
+ "group":"225.1.1.4",
+ "sources":[
+ {
+ "source":"*",
+ "timer":"*",
+ "forwarded":true,
+ "uptime":"*"
+ }
+ ]
+ },
+ "225.1.1.5":{
+ "group":"225.1.1.5",
+ "sources":[
+ {
+ "source":"*",
+ "timer":"*",
+ "forwarded":true,
+ "uptime":"*"
+ }
+ ]
+ }
+ }
+}
+
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json
new file mode 100644
index 0000000..7a19975
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json
@@ -0,0 +1,16 @@
+{
+ "l1-i1-eth1":{
+ "name":"l1-i1-eth1",
+ "225.1.1.4":{
+ "group":"225.1.1.4",
+ "sources":[
+ {
+ "source":"*",
+ "timer":"*",
+ "forwarded":true,
+ "uptime":"*"
+ }
+ ]
+ }
+ }
+}
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..2c1241c
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py
@@ -0,0 +1,4856 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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
+import json
+import functools
+
+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 import topotest
+from lib.topogen import Topogen, TopoRouter, 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 version should be >= 4.19")
+
+ 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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # 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 "
+ "Expected: [{}]: Multicast traffic should be stopped \n "
+ "Found: {}".format(tc_name, "l1", 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 "
+ "Expected: [{}]: IGMP groups should be deleted \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: Upstream IIF {} should not be present \n "
+ "Found: {}".format(tc_name, data["dut"], data["iif"], 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 "
+ "Expected: [{}]: Multicast traffic should be stopped \n "
+ "Found: {}".format(tc_name, "f1", 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 "
+ "Expected: [{}]: IGMP groups should be deleted \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: Upstream IIF {} should not be present \n "
+ "Found: {}".format(tc_name, data["dut"], data["iif"], 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: Upstream IIF {} should not be present \n "
+ "Found: {}".format(tc_name, data["dut"], data["iif"], 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, "f1", 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 \n "
+ "Expected: [{}]: Upstream IIF should be Unknown \n "
+ "Found: {}".format(tc_name, "l1", result)
+ )
+
+ 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 \n "
+ "Expected: [{}]: Upstream IIF should be Unknown \n "
+ "Found: {}".format(tc_name, "l1", result)
+ )
+
+ 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)
+
+ # IGMP JSON verification
+ step("Verify IGMP group and source JSON for single interface and group")
+ router = tgen.gears["l1"]
+
+ reffile = os.path.join(CWD, "igmp_group_all_detail.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip igmp vrf default groups detail json",
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "IGMP group detailed output on l1 for all interfaces and all groups is not as expected. Expected: {}".format(
+ expected
+ )
+ assert res is None, assertmsg
+
+ reffile = os.path.join(CWD, "igmp_single_if_group_all_brief.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip igmp vrf default groups l1-i1-eth1 json",
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "IGMP group output on l1 for all groups in interface l1-i1-eth1 is not as expected. Expected: {}".format(
+ expected
+ )
+ assert res is None, assertmsg
+
+ reffile = os.path.join(CWD, "igmp_single_if_group_all_detail.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip igmp vrf default groups l1-i1-eth1 detail json",
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "IGMP group detailed output on l1 for all groups in interface l1-i1-eth1 is not as expected. Expected: {}".format(
+ expected
+ )
+ assert res is None, assertmsg
+
+ reffile = os.path.join(CWD, "igmp_single_if_single_group_brief.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip igmp vrf default groups l1-i1-eth1 225.1.1.5 json",
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "IGMP group output on l1 for interface l1-i1-eth1 and group 225.1.1.5 is not as expected. Expected: {}".format(
+ expected
+ )
+ assert res is None, assertmsg
+
+ reffile = os.path.join(CWD, "igmp_single_if_single_group_detail.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip igmp vrf default groups l1-i1-eth1 225.1.1.5 detail json",
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "IGMP group detailed output on l1 for interface l1-i1-eth1 and group 225.1.1.5 is not as expected. Expected: {}".format(
+ expected
+ )
+ assert res is None, assertmsg
+
+ reffile = os.path.join(CWD, "igmp_source_single_if_group_all.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip igmp sources l1-i1-eth1 json",
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "IGMP source output on l1 for interface l1-i1-eth1 is not as expected. Expected: {}".format(
+ expected
+ )
+ assert res is None, assertmsg
+
+ reffile = os.path.join(CWD, "igmp_source_single_if_single_group.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip igmp sources l1-i1-eth1 225.1.1.4 json",
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "IGMP source output on l1 for interface l1-i1-eth1 and group 225.1.1.4 is not as expected. Expected: {}".format(
+ expected
+ )
+ assert res is None, assertmsg
+
+ 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 "
+ "Expected: [{}]: IGMP groups should not be present \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: IGMP groups should not be present \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: IGMP interface should be removed \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: IGMP groups should not present \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: Upstream IIF interface {} should not be present\n"
+ "Found: {}".format(tc_name, data["dut"], data["iif"], 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 "
+ "Expected: [{}]: RP IIF should be updated as Unknown \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream IIF interface {} should not be present"
+ " after shutting link from RP to FHR \n"
+ "Found: {}".format(tc_name, data["dut"], data["iif"], 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 "
+ "Expected: [{}]: RP IIF should be updated as Unknown \n"
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream IIF interface {} should not be present"
+ " after shutting link from FHR to RP \n"
+ "Found: {}".format(tc_name, data["dut"], data["iif"], 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 "
+ "Expected: [{}]: RP IIF should be updated as Unknown \n"
+ "Found: {}".format(tc_name, dut, 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: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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..825281a
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo4.py
@@ -0,0 +1,1084 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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 version should be >= 4.19")
+
+ 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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: Upstream IIF interface {} should not be present\n "
+ "Found: {}".format(tc_name, data["dut"], data["iif"], 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: Upstream IIF interface {} should not be present\n "
+ "Found: {}".format(tc_name, data["dut"], data["iif"], 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 "
+ "Expected: [{}]: RP Info should not be Unknown after removing static"
+ " route from c2 \n"
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: mroute (S, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, data["dut"], 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 "
+ "Expected: [{}]: Upstream IIF interface {} should not be present\n "
+ "Found: {}".format(tc_name, data["dut"], data["iif"], 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 "
+ "Expected: [{}]: RP Info should not be Unknown after removing static"
+ " route from c2 \n"
+ "Found: {}".format(tc_name, data["dut"], 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/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..c492d95
--- /dev/null
+++ b/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp.py
@@ -0,0 +1,1376 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # 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 "
+ "Expected: [{}]: IGMP groups should not be present without any IGMP join\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: RP info should not be present \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream IIF interface {} should not be present\n "
+ "Found: {}".format(tc_name, dut, iif, 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 "
+ "Expected: [{}]: Upstream Join State timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: PIM state should not be up \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (*, G) should not be present \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: OIL should be same and IIF should be cleared\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream IIF interface {} should be unknown \n "
+ "Found: {}".format(tc_name, dut, iif, 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 "
+ "Expected: [{}]: Upstream Join State timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (*, G) should be cleared from mroute table\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: RP-info should not be present \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream IIF interface {} should not be present \n "
+ "Found: {}".format(tc_name, dut, iif, 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 "
+ "Expected: [{}]: Upstream Join State timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: PIM state should not be up\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (*, G) should not be present in mroute table \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream IIF interface {} should not be present \n "
+ "Found: {}".format(tc_name, dut, iif, 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 "
+ "Expected: [{}]: Upstream Join State timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: PIM state should not be up \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (*, G) should not be present \n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: RP-info should not be present \n "
+ "Found: {}".format(tc_name, dut, 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..690c92f
--- /dev/null
+++ b/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp1.py
@@ -0,0 +1,1406 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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,
+)
+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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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..8aa2e4e
--- /dev/null
+++ b/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp2.py
@@ -0,0 +1,1783 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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,
+)
+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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: Upstream Join State should not be Joined and "
+ "join timer should not run\n "
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (*, G) should be cleared \n"
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (*, G) should be cleared \n"
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (*, G) should be cleared after shutting"
+ "link from R1 to R3 \n"
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (*, G) should be cleared after shutting"
+ "link from R1 to R0 \n"
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (*, G) should be cleared after shutting"
+ "link from R1 to R0 \n"
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (*, G) should be cleared after shutting"
+ "link from R2 to R3 \n"
+ "Found: {}".format(tc_name, dut, 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 "
+ "Expected: [{}]: mroute (*, G) should be cleared after shutting"
+ "link from R2 to R3 \n"
+ "Found: {}".format(tc_name, dut, 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..5728a4d
--- /dev/null
+++ b/tests/topotests/multicast_pim_uplink_topo1/test_multicast_pim_uplink_topo1.py
@@ -0,0 +1,3362 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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,
+)
+from lib.bgp import (
+ create_router_bgp,
+ verify_bgp_convergence,
+)
+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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # 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)
+
+ # Verify 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()
+
+ 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # 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/multicast_pim_uplink_topo2/multicast_pim_uplink_topo2.json b/tests/topotests/multicast_pim_uplink_topo2/multicast_pim_uplink_topo2.json
new file mode 100644
index 0000000..158e113
--- /dev/null
+++ b/tests/topotests/multicast_pim_uplink_topo2/multicast_pim_uplink_topo2.json
@@ -0,0 +1,288 @@
+{
+ "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"},
+ "r4": {"ipv4": "auto", "pim": "enable"},
+ "r5": {"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": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "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"},
+ "r1": {"ipv4": "auto", "pim": "enable"},
+ "r5": {"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": {}
+ }
+ },
+ "r1": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r5": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r1": {"ipv4": "auto", "pim": "enable"},
+ "r4": {"ipv4": "auto", "pim": "enable"},
+ "i8": {"ipv4": "auto", "pim": "enable"}
+ },
+ "bgp": {
+ "local_as": "500",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r5": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "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"}
+ }
+ },
+ "i8": {
+ "links": {
+ "r5": {"ipv4": "auto"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim_uplink_topo2/test_multicast_pim_uplink_topo2.py b/tests/topotests/multicast_pim_uplink_topo2/test_multicast_pim_uplink_topo2.py
new file mode 100644
index 0000000..1fb81c0
--- /dev/null
+++ b/tests/topotests/multicast_pim_uplink_topo2/test_multicast_pim_uplink_topo2.py
@@ -0,0 +1,1381 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2023 by VMware, Inc. ("VMware")
+#
+
+"""
+Following tests are covered to test multicast pim sm:
+
+1. Verify changing RP address on DUT from Static to BSR , IIF and OIF
+ updated correctly
+2. Verify when mroute RPT and SPT path is difference
+3. Verify mroutes updated with correct OIL and IIF after shut / no shut of
+ upstream interface from DUT
+4. Verify mroutes updated with correct OIL and IIF after shut / no
+shut of downstream interface from FHR
+
+
+"""
+
+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/"))
+
+# 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,
+ required_linux_kernel_version,
+)
+from lib.pim import (
+ create_pim_config,
+ create_igmp_config,
+ verify_mroutes,
+ clear_pim_interface_traffic,
+ verify_upstream_iif,
+ clear_mroute,
+ verify_multicast_traffic,
+ verify_pim_rp_info,
+ verify_pim_interface_traffic,
+ McastTesterHelper,
+)
+from lib.bgp import (
+ verify_bgp_convergence,
+)
+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"]
+
+r1_r2_links = []
+r1_r3_links = []
+r2_r1_links = []
+r3_r1_links = []
+r2_r4_links = []
+r4_r2_links = []
+r4_r3_links = []
+
+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_topo2.json".format(testdir)
+ 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 deamons and then start routers
+ start_topology(tgen)
+
+ # 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)
+
+ # Verify 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()
+
+ 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 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_iif_oil_when_RP_address_changes_from_static_to_BSR_p1(request):
+ """
+ Verify changing RP address on DUT from Static to BSR , IIF and OIF
+ updated correctly
+ """
+
+ 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Shutdown interfaces which are not required")
+ intf_r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"]
+ intf_r1_r5 = topo["routers"]["r1"]["links"]["r5"]["interface"]
+ intf_r4_r1 = topo["routers"]["r4"]["links"]["r1"]["interface"]
+ intf_r5_r1 = topo["routers"]["r5"]["links"]["r1"]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf_r1_r4, False)
+ shutdown_bringup_interface(tgen, "r1", intf_r1_r5, False)
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r1, False)
+ shutdown_bringup_interface(tgen, "r5", intf_r5_r1, False)
+
+ step("Enable IGMP on DUT and R4 interface")
+ intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"]
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ for dut, intf in zip(["r2", "r4"], [intf_r2_i3, 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 = {
+ "i3": topo["routers"]["i3"]["links"]["r2"]["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 static RP as R4 loopback interface for group range 225.1.1.1-5")
+
+ 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("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 DUT for group range 225.1.1.1-5")
+
+ input_src = {
+ "i4": topo["routers"]["i4"]["links"]["r2"]["interface"],
+ "i6": topo["routers"]["i6"]["links"]["r4"]["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_i4 = topo["routers"]["i4"]["links"]["r2"]["ipv4"].split("/")[0]
+ input_dict_star_sg = [
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": r2_r4_links,
+ "oil": topo["routers"]["r2"]["links"]["i3"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": r4_r2_links + [topo["routers"]["r4"]["links"]["i7"]["interface"]],
+ },
+ {
+ "dut": "r2",
+ "src_address": source_i6,
+ "iif": r2_r4_links,
+ "oil": topo["routers"]["r2"]["links"]["i3"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": source_i4,
+ "iif": topo["routers"]["r2"]["links"]["i4"]["interface"],
+ "oil": r2_r4_links + [topo["routers"]["r2"]["links"]["i3"]["interface"]],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r4"]["links"]["i6"]["interface"],
+ "oil": r4_r2_links + [topo["routers"]["r4"]["links"]["i7"]["interface"]],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i4,
+ "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)
+
+ 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(
+ "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_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"]
+ input_traffic = {
+ "r2": {"traffic_sent": [intf_r2_i3]},
+ "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(
+ "Change RP address for range 225.1.1.1-5 to cisco (BSRP) " "loopback interface"
+ )
+
+ input_dict = {
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r4"]["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)
+
+ # Need to wait for 10 sec to make sure prune is received before below RP change is executed
+ sleep(10)
+
+ input_dict = {
+ "r5": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r5"]["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 one more traffic stream from R4 to 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("RP type is changed to BSRP for 225.1.1.1-5 groups range on DUT")
+
+ rp_addr = topo["routers"]["r5"]["links"]["lo"]["ipv4"].split("/")[0]
+
+ result = verify_pim_rp_info(
+ tgen, topo, "r5", GROUP_RANGE_1, "lo", rp_addr, "Static"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "No impact seen on multicast data traffic for both groups range "
+ "verify using 'show ip multicast json' 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
+ )
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Stop traffic and do clear mroute on all the node (make "
+ "sure (s,g) got timeout"
+ )
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+
+ step("Verify (S,G) got cleared after stop of traffic and 'clear mroute'")
+
+ 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
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_mroute_when_RPT_and_SPT_path_is_different_p1(request):
+ """
+ Verify when mroute RPT and SPT path is difference
+ """
+
+ 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Shut link from R3 to R1 and no shut R1 to R4 link to make star topology")
+ for i in range(1, 5):
+ intf = topo["routers"]["r3"]["links"]["r1-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r3", intf, False)
+
+ intf = topo["routers"]["r1"]["links"]["r3-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, False)
+
+ intf_r4_r5 = topo["routers"]["r4"]["links"]["r5"]["interface"]
+ intf_r5_r4 = topo["routers"]["r5"]["links"]["r4"]["interface"]
+ intf_r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"]
+ intf_r1_r5 = topo["routers"]["r1"]["links"]["r5"]["interface"]
+ intf_r4_r1 = topo["routers"]["r4"]["links"]["r1"]["interface"]
+ intf_r5_r1 = topo["routers"]["r5"]["links"]["r1"]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r5, False)
+ shutdown_bringup_interface(tgen, "r5", intf_r5_r4, False)
+ shutdown_bringup_interface(tgen, "r1", intf_r1_r4, True)
+ shutdown_bringup_interface(tgen, "r1", intf_r1_r5, True)
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r1, True)
+ shutdown_bringup_interface(tgen, "r5", intf_r5_r1, True)
+
+ step("Done in base config: Connected one more route R5 before R1 ( R5-R1)")
+
+ step("Enable IGMP on R5 and R4 interface")
+ intf_r5_i8 = topo["routers"]["r5"]["links"]["i8"]["interface"]
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ for dut, intf in zip(["r4", "r5"], [intf_r4_i7, intf_r5_i8]):
+ 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 R5, for group range 226.1.1.1-5")
+ input_join = {
+ "i8": topo["routers"]["i8"]["links"]["r5"]["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("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 R3 for group range 226.1.1.1-5")
+
+ result = app_helper.run_traffic("i5", IGMP_JOIN_RANGE_2, "r3")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("(*,G) IIF updated for 225.1.1.1-5 towards R2 and RP " "type is static on DUT")
+
+ step("(S,G) on R5 has updated for all the groups")
+
+ source_i5 = topo["routers"]["i5"]["links"]["r3"]["ipv4"].split("/")[0]
+ input_dict_star_sg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["r5"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": "*",
+ "iif": r4_r2_links + [intf_r4_r1],
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i5,
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["r5"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i5,
+ "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_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "(S,G) on R1 updated and has IIF toward R4 and OIL toward R5 , "
+ "RP path OIL is removed"
+ )
+
+ source_i5 = topo["routers"]["i5"]["links"]["r3"]["ipv4"].split("/")[0]
+ input_dict_sg = [
+ {"dut": "r1", "src_address": source_i5, "iif": r1_r2_links, "oil": r1_r2_links},
+ {"dut": "r4", "src_address": source_i5, "iif": r4_r2_links, "oil": r4_r2_links},
+ ]
+
+ for data in input_dict_sg:
+ 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 " "OIF and IIF are same \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Shut and no Shut of mroute OIL selected links from R1 towards R2 and R4")
+
+ 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 and no shut of link verify mroute got populated as per "
+ "verification step 8"
+ )
+
+ 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)
+
+ for data in input_dict_sg:
+ 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 " "OIF and IIF are same \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_mroutes_updated_with_correct_oil_iif_after_shut_noshut_upstream_interface_p0(
+ request,
+):
+ """
+ Verify mroutes updated with correct OIL and IIF after shut / no shut of
+ upstream interface from 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Shutdown interfaces which are not required")
+ intf_r1_r5 = topo["routers"]["r1"]["links"]["r5"]["interface"]
+ intf_r5_r1 = topo["routers"]["r5"]["links"]["r1"]["interface"]
+ intf_r4_r5 = topo["routers"]["r4"]["links"]["r5"]["interface"]
+ intf_r5_r4 = topo["routers"]["r5"]["links"]["r4"]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf_r1_r5, False)
+ shutdown_bringup_interface(tgen, "r5", intf_r5_r1, False)
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r5, False)
+ shutdown_bringup_interface(tgen, "r5", intf_r5_r4, False)
+
+ step("Enable IGMP on DUT receiver interface")
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"]
+ for dut, intf in zip(["r1", "r1"], [intf_r1_i1, intf_r1_i2]):
+ 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("Verify pim interface traffic before sending join/traffic")
+
+ 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("Send IGMP joins from DUT for group range 225.1.1.1-5")
+ result = app_helper.run_join("i1", IGMP_JOIN_RANGE_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure RP as R2 and R3 interface (225.1.1.1-3 on R2 and "
+ "225.1.1.4-5 on R3)"
+ )
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1[0:3],
+ }
+ ]
+ }
+ },
+ "r3": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r3"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1[3:5],
+ }
+ ]
+ }
+ },
+ }
+
+ 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 BGP 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(
+ "(*,G) IIF is updated DUT-R2 any one interface for groups 225.1.1.1-3 "
+ "and DUT to R3 any one interface for groups 225.1.1.1-3"
+ )
+
+ input_dict_starg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif_r1_r2": r1_r2_links,
+ "iif_r1_r3": r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ }
+ ]
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[0:3],
+ 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[0:3],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[3:5],
+ data["iif_r1_r3"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif_r1_r3"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[3:5],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "(S,G) IIF updated towards shortest path to source verify using "
+ "'show ip mroute' and 'show ip mroute json'"
+ )
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["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)
+
+ 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) and (S,G) OIL is updated and traffic is received for all "
+ "the groups verify using 'show ip multicast' and"
+ "'show ip multicast count json'"
+ )
+
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1], "traffic_received": [intf_r1_r4]}
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Register packets sent/received count is incrementing verify "
+ "using 'show ip pim interface traffic json'"
+ )
+
+ 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("Shut interface connected from R4 to DUT")
+ intf_r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf_r1_r4, False)
+
+ step(
+ "After shut of R4 to DUT interface verify (S,G) has taken "
+ "different path ( via R2 or R3 any link) , uptime got resetted "
+ "and OIL is updated accordingly No impact seen on (*,G) routes , "
+ "verify uptime for (*,G) using 'show ip mroute json' and "
+ "'show ip pim state'"
+ )
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[0:3],
+ 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[0:3],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[3:5],
+ data["iif_r1_r3"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif_r1_r3"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[3:5],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["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)
+
+ 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 interface connected from DUT to R2 one by one")
+
+ 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 of DUT to R2 all the interfaces (S,G) created via R3, "
+ "(S,G) uptime get reset and OIL is updated accordingly, No impact "
+ "seen on (*,G) routes verify using 'show ip mroute json'"
+ )
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[0:3],
+ data["iif_r1_r3"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif_r1_r3"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[0:3],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[3:5],
+ data["iif_r1_r3"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif_r1_r3"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[3:5],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["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)
+
+ 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_after_shut_noshut_downstream_interface_p0(
+ request,
+):
+ """
+ Verify mroutes updated with correct OIL and IIF after shut / no
+ shut of downstream interface from FHR
+ """
+
+ 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Shutdown interfaces which are not required")
+ intf_r1_r5 = topo["routers"]["r1"]["links"]["r5"]["interface"]
+ intf_r5_r1 = topo["routers"]["r5"]["links"]["r1"]["interface"]
+ intf_r4_r5 = topo["routers"]["r4"]["links"]["r5"]["interface"]
+ intf_r5_r4 = topo["routers"]["r5"]["links"]["r4"]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf_r1_r5, False)
+ shutdown_bringup_interface(tgen, "r5", intf_r5_r1, False)
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r5, False)
+ shutdown_bringup_interface(tgen, "r5", intf_r5_r4, False)
+
+ step("Enable IGMP on DUT receiver interface")
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"]
+ for dut, intf in zip(["r1", "r1"], [intf_r1_i1, intf_r1_i2]):
+ 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 for group range 225.1.1.1-5")
+ result = app_helper.run_join("i1", IGMP_JOIN_RANGE_1, "r1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure RP as R2 and R3 interface (225.1.1.1-3 on R2 and "
+ "225.1.1.4-5 on R3)"
+ )
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1[0:3],
+ }
+ ]
+ }
+ },
+ "r3": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r3"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1[3:5],
+ }
+ ]
+ }
+ },
+ }
+
+ 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 BGP 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(
+ "(*,G) IIF is updated DUT-R2 any one interface for groups 225.1.1.1-3 "
+ "and DUT to R3 any one interface for groups 225.1.1.1-3"
+ )
+
+ input_dict_starg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif_r1_r2": r1_r2_links,
+ "iif_r1_r3": r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ }
+ ]
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[0:3],
+ 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[0:3],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[3:5],
+ data["iif_r1_r3"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif_r1_r3"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[3:5],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "(S,G) IIF updated towards shortest path to source verify using "
+ "'show ip mroute' and 'show ip mroute json'"
+ )
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["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)
+
+ 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) and (S,G) OIL is updated and traffic is received for all "
+ "the groups verify using 'show ip multicast' and"
+ "'show ip multicast count json'"
+ )
+
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1], "traffic_received": [intf_r1_r4]}
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut interface connected from R4 to DUT")
+ intf_r4_r1 = topo["routers"]["r4"]["links"]["r1"]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r1, False)
+
+ step(
+ "After shut of R4 to DUT interface verify (S,G) has taken "
+ "different path ( via R2 or R3 any link) , uptime got resetted "
+ "and OIL is updated accordingly No impact seen on (*,G) routes , "
+ "verify uptime for (*,G) using 'show ip mroute json' and "
+ "'show ip pim state'"
+ )
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[0:3],
+ 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[0:3],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[3:5],
+ data["iif_r1_r3"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif_r1_r3"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1[3:5],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["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"],
+ }
+ ]
+
+ 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)
+
+ 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_topo3/multicast_pim_uplink_topo3.json b/tests/topotests/multicast_pim_uplink_topo3/multicast_pim_uplink_topo3.json
new file mode 100644
index 0000000..dc9e1ac
--- /dev/null
+++ b/tests/topotests/multicast_pim_uplink_topo3/multicast_pim_uplink_topo3.json
@@ -0,0 +1,295 @@
+{
+ "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"},
+ "r4": {"ipv4": "auto", "pim": "enable"},
+ "r5": {"ipv4": "auto", "pim": "enable"},
+ "i1": {"ipv4": "auto", "pim": "enable"},
+ "i2": {"ipv4": "auto", "pim": "enable"},
+ "i9": {"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": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "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"},
+ "r1": {"ipv4": "auto", "pim": "enable"},
+ "r5": {"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": {}
+ }
+ },
+ "r1": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r5": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r1": {"ipv4": "auto", "pim": "enable"},
+ "r4": {"ipv4": "auto", "pim": "enable"},
+ "i8": {"ipv4": "auto", "pim": "enable"}
+ },
+ "bgp": {
+ "local_as": "500",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r5": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "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"}
+ }
+ },
+ "i8": {
+ "links": {
+ "r5": {"ipv4": "auto"}
+ }
+ },
+ "i9": {
+ "links": {
+ "r1": {"ipv4": "auto"}
+ }
+ }
+
+ }
+}
diff --git a/tests/topotests/multicast_pim_uplink_topo3/test_multicast_pim_uplink_topo3.py b/tests/topotests/multicast_pim_uplink_topo3/test_multicast_pim_uplink_topo3.py
new file mode 100644
index 0000000..bed2f2f
--- /dev/null
+++ b/tests/topotests/multicast_pim_uplink_topo3/test_multicast_pim_uplink_topo3.py
@@ -0,0 +1,940 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2023 by VMware, Inc. ("VMware")
+#
+
+"""
+Following tests are covered to test multicast pim sm:
+
+1. TC:1 Verify static IGMP group populated when static "ip igmp join <grp>" in configured
+2. TC:2 Verify mroute and upstream populated with correct OIL/IIF with static igmp join
+3. TC:3 Verify local IGMP join not allowed for "224.0.0.0/24" and non multicast group
+4. TC:4 Verify static IGMP group removed from DUT while removing "ip igmp join" CLI
+5. TC:5 Verify static IGMP groups after removing and adding IGMP config
+"""
+
+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,
+ reset_config_on_routers,
+ shutdown_bringup_interface,
+ required_linux_kernel_version,
+)
+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_pim_rp_info,
+ verify_local_igmp_groups,
+ McastTesterHelper,
+)
+from lib.bgp import (
+ verify_bgp_convergence,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+# Global variables
+TOPOLOGY = """
+
+ i9 i3-+-i4 i6-+-i7
+ | | |
+ i1--- R1-------R2----------R4------R5---i8
+ | | |
+ i2 R3-------------------+
+ +
+ |
+ i5
+
+ Description:
+ i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send IGMP
+ join and traffic
+ R1 - DUT (LHR/FHR)
+ R2 - RP
+ R3 - Transit
+ R4 - (LHR/FHR)
+ R5 - Transit
+"""
+# Global variables
+RP_RANGE1 = "226.0.0.1/32"
+RP_RANGE2 = "226.0.0.2/32"
+RP_RANGE3 = "226.0.0.3/32"
+RP_RANGE4 = "226.0.0.4/32"
+RP_RANGE5 = "226.0.0.5/32"
+RP_RANGE6 = "232.0.0.1/32"
+RP_RANGE7 = "232.0.0.2/32"
+RP_RANGE8 = "232.0.0.3/32"
+RP_RANGE9 = "232.0.0.4/32"
+RP_RANGE10 = "232.0.0.5/32"
+
+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"]
+IGMP_JOIN_RANGE_2 = ["224.0.0.1", "224.0.0.2", "224.0.0.3", "192.0.0.4", "192.0.0.5"]
+IGMP_JOIN_RANGE_3 = [
+ "226.0.0.1",
+ "226.0.0.2",
+ "226.0.0.3",
+ "226.0.0.4",
+ "226.0.0.5",
+ "232.0.0.1",
+ "232.0.0.2",
+ "232.0.0.3",
+ "232.0.0.4",
+ "232.0.0.5",
+]
+GROUP_RANGE_3 = [
+ "226.0.0.1/32",
+ "226.0.0.2/32",
+ "226.0.0.3/32",
+ "226.0.0.4/32",
+ "226.0.0.5/32",
+ "232.0.0.1/32",
+ "232.0.0.2/32",
+ "232.0.0.3/32",
+ "232.0.0.4/32",
+ "232.0.0.5/32",
+]
+
+r1_r2_links = []
+r1_r3_links = []
+r2_r1_links = []
+r2_r4_links = []
+r3_r1_links = []
+r3_r4_links = []
+r4_r2_links = []
+r4_r3_links = []
+
+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_topo3.json".format(testdir)
+ 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 deamons and then start routers
+ start_topology(tgen)
+
+ # 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)
+
+ # Verify 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()
+
+ 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 shutdown_interfaces(tgen):
+ """
+ API to Shut down interfaces which is not
+ used in all the testcases as part of this TDS
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+
+ """
+ logger.info("shutting down extra interfaces")
+ intf_r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"]
+ intf_r1_r5 = topo["routers"]["r1"]["links"]["r5"]["interface"]
+ intf_r4_r1 = topo["routers"]["r4"]["links"]["r1"]["interface"]
+ intf_r5_r1 = topo["routers"]["r5"]["links"]["r1"]["interface"]
+ intf_r4_r5 = topo["routers"]["r4"]["links"]["r5"]["interface"]
+ intf_r5_r4 = topo["routers"]["r5"]["links"]["r4"]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf_r1_r4, False)
+ shutdown_bringup_interface(tgen, "r1", intf_r1_r5, False)
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r1, False)
+ shutdown_bringup_interface(tgen, "r5", intf_r5_r1, False)
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r5, False)
+ shutdown_bringup_interface(tgen, "r5", intf_r5_r4, False)
+
+
+def config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, iperf, iperf_intf, GROUP_RANGE, join=False, traffic=False
+):
+ """
+ API to do pre-configuration to send IGMP join and multicast
+ traffic
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ * `topo`: input json data
+ * `tc_name`: caller test case name
+ * `iperf`: router running iperf
+ * `iperf_intf`: interface name router running iperf
+ * `GROUP_RANGE`: group range
+ * `join`: IGMP join, default False
+ * `traffic`: multicast traffic, default False
+ """
+
+ if join:
+ # Add route to kernal
+ result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ if traffic:
+ # Add route to kernal
+ result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ router_list = tgen.routers()
+ for router in router_list.keys():
+ if router == iperf:
+ continue
+
+ rnode = router_list[router]
+ rnode.run("echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter")
+
+ return True
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_ip_igmp_local_joins_p0(request):
+ """
+ TC_1 Verify static IGMP group populated when static
+ "ip igmp join <grp>" in configured
+ """
+
+ 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("shut down not required interfaces")
+ shutdown_interfaces(tgen)
+
+ step("Enable the PIM on all the interfaces of R1, R2, R3, R4")
+ step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected")
+ step("Enable the IGMP on R11 interfac of R1 and configure local igmp groups")
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"]
+ input_dict = {
+ "r1": {
+ "igmp": {
+ "interfaces": {
+ intf_r1_i1: {"igmp": {"version": "2", "join": IGMP_JOIN_RANGE_1}},
+ intf_r1_i2: {"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("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("verify static igmp join using show ip igmp join")
+ dut = "r1"
+ interfaces = [intf_r1_i1, intf_r1_i2]
+ for interface in interfaces:
+ result = verify_local_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify igmp groups using show ip igmp groups")
+ interfaces = [intf_r1_i1, intf_r1_i2]
+ for interface in interfaces:
+ result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_mroute_with_igmp_local_joins_p0(request):
+ """
+ TC_2 Verify mroute and upstream populated with correct OIL/IIF with
+ static igmp 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("shut down not required interfaces")
+ shutdown_interfaces(tgen)
+
+ step("Enable the PIM on all the interfaces of R1, R2, R3, R4")
+ step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected")
+ step("Enable the IGMP on R11 interfac of R1 and configure local igmp groups")
+
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"]
+ input_dict = {
+ "r1": {
+ "igmp": {
+ "interfaces": {
+ intf_r1_i1: {"igmp": {"version": "2", "join": IGMP_JOIN_RANGE_1}},
+ intf_r1_i2: {"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("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("verify static igmp join using show ip igmp join")
+ dut = "r1"
+ interfaces = [intf_r1_i1, intf_r1_i2]
+ for interface in interfaces:
+ result = verify_local_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify igmp groups using show ip igmp groups")
+ interfaces = [intf_r1_i1, intf_r1_i2]
+ for interface in interfaces:
+ result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify RP-info populated in DUT")
+ dut = "r1"
+ rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv4"].split("/")[0]
+ SOURCE = "Static"
+ oif = r1_r2_links
+ result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Send traffic from R4 to all the groups ( 225.1.1.1 to 225.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)
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+
+ r1_r2_r3 = r1_r2_links + r1_r3_links
+ input_dict_starg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": r1_r2_r3,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": r1_r2_links,
+ "oil": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ },
+ ]
+
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r2_r3,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r2_r3,
+ "oil": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream for local igmp groups")
+ for input_dict in [input_dict_starg, input_dict_sg]:
+ for data in input_dict:
+ 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 mroutes not created with local interface ip ")
+
+ input_dict_local_sg = [
+ {
+ "dut": "r1",
+ "src_address": intf_r1_i1,
+ "iif": r1_r2_r3,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": intf_r1_i2,
+ "iif": r1_r2_r3,
+ "oil": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ },
+ ]
+
+ for data in input_dict_local_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 Error: {}"
+ "sg created with local interface ip".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 Error: {}"
+ "upstream created with local interface ip".format(tc_name, result)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_igmp_local_join_with_reserved_address_p0(request):
+ """
+ TC_3 Verify local IGMP join not allowed for "224.0.0.0/24"
+ and non multicast group
+ """
+ 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("shut down not required interfaces")
+ shutdown_interfaces(tgen)
+
+ step("Enable the PIM on all the interfaces of R1, R2, R3, R4")
+ step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected")
+ step("Enable the IGMP on R11 interface of R1 and configure local igmp groups")
+
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_dict = {
+ "r1": {
+ "igmp": {
+ "interfaces": {
+ intf_r1_i1: {"igmp": {"version": "2", "join": IGMP_JOIN_RANGE_2}}
+ }
+ }
+ }
+ }
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("verify static igmp join using show ip igmp join")
+ dut = "r1"
+ interface = intf_r1_i1
+ result = verify_local_igmp_groups(
+ tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Error: {}" "IGMP join still present".format(
+ tc_name, result
+ )
+
+ step("verify igmp groups using show ip igmp groups")
+ interface = intf_r1_i1
+ result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Error: {}" "IGMP groups still present".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_remove_add_igmp_local_joins_p1(request):
+ """
+ TC_4 Verify static IGMP group removed from DUT while
+ removing "ip igmp join" CLI
+ """
+
+ 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)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("shut down not required interfaces")
+ shutdown_interfaces(tgen)
+
+ step("Enable the PIM on all the interfaces of R1, R2, R3, R4")
+ step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected")
+ step("Enable the IGMP on R11 interfac of R1 and configure local igmp groups")
+
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"]
+ input_dict = {
+ "r1": {
+ "igmp": {
+ "interfaces": {
+ intf_r1_i1: {"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("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("verify static igmp join using show ip igmp join")
+ dut = "r1"
+ interface = intf_r1_i1
+ result = verify_local_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("verify igmp groups using show ip igmp groups")
+
+ interface = intf_r1_i1
+ result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("verify RP-info populated in DUT")
+ dut = "r1"
+ rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv4"].split("/")[0]
+ SOURCE = "Static"
+ oif = r1_r2_links
+ result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Send traffic from R4 to all the groups ( 225.1.1.1 to 225.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)
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+
+ logger.info("waiting 30 sec for SPT switchover")
+
+ r1_r2_r3 = r1_r2_links + r1_r3_links
+ input_dict_starg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": r1_r2_r3,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ }
+ ]
+
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r2_r3,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ }
+ ]
+
+ step("Verify mroutes and iff upstream for local igmp groups")
+ for input_dict in [input_dict_starg, input_dict_sg]:
+ for data in input_dict:
+ 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("Remove IGMP join from DUT")
+ input_dict = {
+ "r1": {
+ "igmp": {
+ "interfaces": {
+ intf_r1_i1: {
+ "igmp": {
+ "join": IGMP_JOIN_RANGE_1,
+ "delete_attr": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("verify static igmp join removed using show ip igmp join")
+ dut = "r1"
+ interface = intf_r1_i1
+ result = verify_local_igmp_groups(
+ tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Error: {}" "IGMP join still present".format(
+ tc_name, result
+ )
+
+ step("verify igmp groups removed using show ip igmp groups")
+ interface = intf_r1_i1
+ result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Error: {}" "IGMP groups still present".format(
+ tc_name, result
+ )
+
+ step("Verify mroutes and iff upstream for local igmp groups")
+ for input_dict in [input_dict_starg, input_dict_sg]:
+ 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 Error: {}" "mroutes still present".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 Error: {}" "mroutes still present".format(
+ tc_name, result
+ )
+
+ step("Add IGMP join on DUT again")
+ input_dict = {
+ "r1": {
+ "igmp": {
+ "interfaces": {
+ intf_r1_i1: {
+ "igmp": {
+ "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("verify static igmp join using show ip igmp join")
+ dut = "r1"
+ interface = intf_r1_i1
+ result = verify_local_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("verify igmp groups using show ip igmp groups")
+
+ interface = intf_r1_i1
+ result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify mroutes and iff upstream for local igmp groups")
+ for input_dict in [input_dict_starg, input_dict_sg]:
+ for data in input_dict:
+ 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)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/munet/__init__.py b/tests/topotests/munet/__init__.py
new file mode 100644
index 0000000..e1f18e5
--- /dev/null
+++ b/tests/topotests/munet/__init__.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# September 30 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""A module to import various objects to root namespace."""
+from .base import BaseMunet
+from .base import Bridge
+from .base import Commander
+from .base import LinuxNamespace
+from .base import SharedNamespace
+from .base import cmd_error
+from .base import comm_error
+from .base import get_exec_path
+from .base import proc_error
+from .native import L3Bridge
+from .native import L3NamespaceNode
+from .native import Munet
+from .native import to_thread
+
+
+__all__ = [
+ "BaseMunet",
+ "Bridge",
+ "Commander",
+ "L3Bridge",
+ "L3NamespaceNode",
+ "LinuxNamespace",
+ "Munet",
+ "SharedNamespace",
+ "cmd_error",
+ "comm_error",
+ "get_exec_path",
+ "proc_error",
+ "to_thread",
+]
diff --git a/tests/topotests/munet/__main__.py b/tests/topotests/munet/__main__.py
new file mode 100644
index 0000000..4419ab9
--- /dev/null
+++ b/tests/topotests/munet/__main__.py
@@ -0,0 +1,236 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# September 2 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""The main function for standalone operation."""
+import argparse
+import asyncio
+import logging
+import logging.config
+import os
+import subprocess
+import sys
+
+from . import cli
+from . import parser
+from .base import get_event_loop
+from .cleanup import cleanup_previous
+from .compat import PytestConfig
+
+
+logger = None
+
+
+async def forever():
+ while True:
+ await asyncio.sleep(3600)
+
+
+async def run_and_wait(args, unet):
+ tasks = []
+
+ if not args.topology_only:
+ # add the cmd.wait()s returned from unet.run()
+ tasks += await unet.run()
+
+ if sys.stdin.isatty() and not args.no_cli:
+ # Run an interactive CLI
+ task = cli.async_cli(unet)
+ else:
+ if args.no_wait:
+ logger.info("Waiting for all node cmd to complete")
+ task = asyncio.gather(*tasks, return_exceptions=True)
+ else:
+ logger.info("Waiting on signal to exit")
+ task = asyncio.create_task(forever())
+ task = asyncio.gather(task, *tasks, return_exceptions=True)
+
+ try:
+ await task
+ finally:
+ # Basically we are canceling tasks from unet.run() which are just async calls to
+ # node.cmd_p.wait() so we've stopped waiting for them to complete, but not
+ # actually canceld/killed the cmd_p process.
+ for task in tasks:
+ task.cancel()
+
+
+async def async_main(args, config):
+ status = 3
+
+ # Setup the namespaces and network addressing.
+
+ unet = await parser.async_build_topology(
+ config, rundir=args.rundir, args=args, pytestconfig=PytestConfig(args)
+ )
+ logger.info("Topology up: rundir: %s", unet.rundir)
+
+ try:
+ status = await run_and_wait(args, unet)
+ except KeyboardInterrupt:
+ logger.info("Exiting, received KeyboardInterrupt in async_main")
+ except asyncio.CancelledError as ex:
+ logger.info("task canceled error: %s cleaning up", ex)
+ except Exception as error:
+ logger.info("Exiting, unexpected exception %s", error, exc_info=True)
+ else:
+ logger.info("Exiting normally")
+
+ logger.debug("main: async deleting")
+ try:
+ await unet.async_delete()
+ except KeyboardInterrupt:
+ status = 2
+ logger.warning("Received KeyboardInterrupt while cleaning up.")
+ except Exception as error:
+ status = 2
+ logger.info("Deleting, unexpected exception %s", error, exc_info=True)
+ return status
+
+
+def main(*args):
+ ap = argparse.ArgumentParser(args)
+ cap = ap.add_argument_group(title="Config", description="config related options")
+
+ cap.add_argument("-c", "--config", help="config file (yaml, toml, json, ...)")
+ cap.add_argument(
+ "-d", "--rundir", help="runtime directory for tempfiles, logs, etc"
+ )
+ cap.add_argument(
+ "--kinds-config",
+ help="kinds config file, overrides default search (yaml, toml, json, ...)",
+ )
+ cap.add_argument(
+ "--project-root", help="directory to stop searching for kinds config at"
+ )
+ rap = ap.add_argument_group(title="Runtime", description="runtime related options")
+ rap.add_argument(
+ "-C",
+ "--cleanup",
+ action="store_true",
+ help="Remove the entire rundir (not just node subdirs) prior to running.",
+ )
+ rap.add_argument(
+ "--gdb", metavar="NODE-LIST", help="comma-sep list of hosts to run gdb on"
+ )
+ rap.add_argument(
+ "--gdb-breakpoints",
+ metavar="BREAKPOINT-LIST",
+ help="comma-sep list of breakpoints to set",
+ )
+ rap.add_argument(
+ "--host",
+ action="store_true",
+ help="no isolation for top namespace, bridges exposed to default namespace",
+ )
+ rap.add_argument(
+ "--pcap",
+ metavar="TARGET-LIST",
+ help="comma-sep list of capture targets (NETWORK or NODE:IFNAME)",
+ )
+ rap.add_argument(
+ "--shell", metavar="NODE-LIST", help="comma-sep list of nodes to open shells on"
+ )
+ rap.add_argument(
+ "--stderr",
+ metavar="NODE-LIST",
+ help="comma-sep list of nodes to open windows viewing stderr",
+ )
+ rap.add_argument(
+ "--stdout",
+ metavar="NODE-LIST",
+ help="comma-sep list of nodes to open windows viewing stdout",
+ )
+ rap.add_argument(
+ "--topology-only",
+ action="store_true",
+ help="Do not run any node commands",
+ )
+ rap.add_argument("--unshare-inline", action="store_true", help=argparse.SUPPRESS)
+ rap.add_argument(
+ "--validate-only",
+ action="store_true",
+ help="Validate the config against the schema definition",
+ )
+ rap.add_argument("-v", "--verbose", action="store_true", help="be verbose")
+ rap.add_argument(
+ "-V", "--version", action="store_true", help="print the verison number and exit"
+ )
+ eap = ap.add_argument_group(title="Uncommon", description="uncommonly used options")
+ eap.add_argument("--log-config", help="logging config file (yaml, toml, json, ...)")
+ eap.add_argument(
+ "--no-kill",
+ action="store_true",
+ help="Do not kill previous running processes",
+ )
+ eap.add_argument(
+ "--no-cli", action="store_true", help="Do not run the interactive CLI"
+ )
+ eap.add_argument("--no-wait", action="store_true", help="Exit after commands")
+
+ args = ap.parse_args()
+
+ if args.version:
+ from importlib import metadata # pylint: disable=C0415
+
+ print(metadata.version("munet"))
+ sys.exit(0)
+
+ rundir = args.rundir if args.rundir else "/tmp/munet"
+ args.rundir = rundir
+
+ if args.cleanup:
+ if os.path.exists(rundir):
+ if not os.path.exists(f"{rundir}/config.json"):
+ logging.critical(
+ 'unsafe: won\'t clean up rundir "%s" as '
+ "previous config.json not present",
+ rundir,
+ )
+ sys.exit(1)
+ else:
+ subprocess.run(["/usr/bin/rm", "-rf", rundir], check=True)
+
+ subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True)
+ os.environ["MUNET_RUNDIR"] = rundir
+
+ parser.setup_logging(args)
+
+ global logger # pylint: disable=W0603
+ logger = logging.getLogger("munet")
+
+ config = parser.get_config(args.config)
+ logger.info("Loaded config from %s", config["config_pathname"])
+ if not config["topology"]["nodes"]:
+ logger.critical("No nodes defined in config file")
+ return 1
+
+ if not args.no_kill:
+ cleanup_previous()
+
+ loop = None
+ status = 4
+ try:
+ parser.validate_config(config, logger, args)
+ if args.validate_only:
+ return 0
+ # Executes the cmd for each node.
+ loop = get_event_loop()
+ status = loop.run_until_complete(async_main(args, config))
+ except KeyboardInterrupt:
+ logger.info("Exiting, received KeyboardInterrupt in main")
+ except Exception as error:
+ logger.info("Exiting, unexpected exception %s", error, exc_info=True)
+ finally:
+ if loop:
+ loop.close()
+
+ return status
+
+
+if __name__ == "__main__":
+ exit_status = main()
+ sys.exit(exit_status)
diff --git a/tests/topotests/munet/base.py b/tests/topotests/munet/base.py
new file mode 100644
index 0000000..06ca4de
--- /dev/null
+++ b/tests/topotests/munet/base.py
@@ -0,0 +1,3111 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# July 9 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""A module that implements core functionality for library or standalone use."""
+import asyncio
+import datetime
+import errno
+import ipaddress
+import logging
+import os
+import platform
+import re
+import readline
+import shlex
+import signal
+import subprocess
+import sys
+import tempfile
+import time as time_mod
+
+from collections import defaultdict
+from pathlib import Path
+from typing import Union
+
+from . import config as munet_config
+from . import linux
+
+
+try:
+ import pexpect
+
+ from pexpect.fdpexpect import fdspawn
+ from pexpect.popen_spawn import PopenSpawn
+
+ have_pexpect = True
+except ImportError:
+ have_pexpect = False
+
+PEXPECT_PROMPT = "PEXPECT_PROMPT>"
+PEXPECT_CONTINUATION_PROMPT = "PEXPECT_PROMPT+"
+
+root_hostname = subprocess.check_output("hostname")
+our_pid = os.getpid()
+
+
+detailed_cmd_logging = False
+
+
+class MunetError(Exception):
+ """A generic munet error."""
+
+
+class CalledProcessError(subprocess.CalledProcessError):
+ """Improved logging subclass of subprocess.CalledProcessError."""
+
+ def __str__(self):
+ o = self.output.strip() if self.output else ""
+ e = self.stderr.strip() if self.stderr else ""
+ s = f"returncode: {self.returncode} command: {self.cmd}"
+ o = "\n\tstdout: " + o if o else ""
+ e = "\n\tstderr: " + e if e else ""
+ return s + o + e
+
+ def __repr__(self):
+ o = self.output.strip() if self.output else ""
+ e = self.stderr.strip() if self.stderr else ""
+ return f"munet.base.CalledProcessError({self.returncode}, {self.cmd}, {o}, {e})"
+
+
+class Timeout:
+ """An object to passively monitor for timeouts."""
+
+ def __init__(self, delta):
+ self.delta = datetime.timedelta(seconds=delta)
+ self.started_on = datetime.datetime.now()
+ self.expires_on = self.started_on + self.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 remaining(self):
+ remaining = self.expires_on - datetime.datetime.now()
+ return remaining.total_seconds()
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ remaining = self.remaining()
+ if remaining <= 0:
+ raise StopIteration()
+ return remaining
+
+
+def fsafe_name(name):
+ return "".join(x if x.isalnum() else "_" for x in name)
+
+
+def indent(s):
+ return "\t" + s.replace("\n", "\n\t")
+
+
+def shell_quote(command):
+ """Return command wrapped in single quotes."""
+ if sys.version_info[0] >= 3:
+ return shlex.quote(command)
+ return "'" + command.replace("'", "'\"'\"'") + "'"
+
+
+def cmd_error(rc, o, e):
+ s = f"rc {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 shorten(s):
+ s = s.strip()
+ i = s.find("\n")
+ if i > 0:
+ s = s[: i - 1]
+ if not s.endswith("..."):
+ s += "..."
+ if len(s) > 72:
+ s = s[:69]
+ if not s.endswith("..."):
+ s += "..."
+ return s
+
+
+def comm_result(rc, o, e):
+ s = f"\n\treturncode {rc}" if rc else ""
+ o = "\n\tstdout: " + shorten(o) if o and o.strip() else ""
+ e = "\n\tstderr: " + shorten(e) if e and e.strip() else ""
+ return s + o + e
+
+
+def proc_str(p):
+ if hasattr(p, "args"):
+ args = p.args if isinstance(p.args, str) else " ".join(p.args)
+ else:
+ args = ""
+ return f"proc pid: {p.pid} args: {args}"
+
+
+def proc_error(p, o, e):
+ if hasattr(p, "args"):
+ args = p.args if isinstance(p.args, str) else " ".join(p.args)
+ else:
+ args = ""
+
+ s = f"rc {p.returncode} pid {p.pid}"
+ a = "\n\targs: " + args if args else ""
+ o = "\n\tstdout: " + (o.strip() if o and o.strip() else "*empty*")
+ e = "\n\tstderr: " + (e.strip() if e and e.strip() else "*empty*")
+ return s + a + 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)
+
+
+async def acomm_error(p):
+ rc = p.returncode
+ assert rc is not None
+ if not hasattr(p, "saved_output"):
+ p.saved_output = await p.communicate()
+ return proc_error(p, *p.saved_output)
+
+
+def get_kernel_version():
+ kvs = (
+ subprocess.check_output("uname -r", shell=True, text=True).strip().split("-", 1)
+ )
+ kv = kvs[0].split(".")
+ kv = [int(x) for x in kv]
+ return kv
+
+
+def convert_number(value) -> int:
+ """Convert a number value with a possible suffix to an integer.
+
+ >>> convert_number("100k") == 100 * 1024
+ True
+ >>> convert_number("100M") == 100 * 1000 * 1000
+ True
+ >>> convert_number("100Gi") == 100 * 1024 * 1024 * 1024
+ True
+ >>> convert_number("55") == 55
+ True
+ """
+ if value is None:
+ raise ValueError("Invalid value None for convert_number")
+ rate = str(value)
+ base = 1000
+ if rate[-1] == "i":
+ base = 1024
+ rate = rate[:-1]
+ suffix = "KMGTPEZY"
+ index = suffix.find(rate[-1])
+ if index == -1:
+ base = 1024
+ index = suffix.lower().find(rate[-1])
+ if index != -1:
+ rate = rate[:-1]
+ return int(rate) * base ** (index + 1)
+
+
+def is_file_like(fo):
+ return isinstance(fo, int) or hasattr(fo, "fileno")
+
+
+def get_tc_bits_value(user_value):
+ value = convert_number(user_value) / 1000
+ return f"{value:03f}kbit"
+
+
+def get_tc_bytes_value(user_value):
+ # Raw numbers are bytes in tc
+ return convert_number(user_value)
+
+
+def get_tmp_dir(uniq):
+ return os.path.join(tempfile.mkdtemp(), uniq)
+
+
+async def _async_get_exec_path(binary, cmdf, cache):
+ if isinstance(binary, str):
+ bins = [binary]
+ else:
+ bins = binary
+ for b in bins:
+ if b in cache:
+ return cache[b]
+
+ rc, output, _ = await cmdf("which " + b, warn=False)
+ if not rc:
+ cache[b] = os.path.abspath(output.strip())
+ return cache[b]
+ return None
+
+
+def _get_exec_path(binary, cmdf, cache):
+ if isinstance(binary, str):
+ bins = [binary]
+ else:
+ bins = binary
+ for b in bins:
+ if b in cache:
+ return cache[b]
+
+ rc, output, _ = cmdf("which " + b, warn=False)
+ if not rc:
+ cache[b] = os.path.abspath(output.strip())
+ return cache[b]
+ return None
+
+
+def get_event_loop():
+ """Configure and return our non-thread using event loop.
+
+ This function configures a new child watcher to not use threads.
+ Threads cannot be used when we inline unshare a PID namespace.
+ """
+ policy = asyncio.get_event_loop_policy()
+ loop = policy.get_event_loop()
+ owatcher = policy.get_child_watcher()
+ logging.debug(
+ "event_loop_fixture: global policy %s, current loop %s, current watcher %s",
+ policy,
+ loop,
+ owatcher,
+ )
+
+ policy.set_child_watcher(None)
+ owatcher.close()
+
+ try:
+ watcher = asyncio.PidfdChildWatcher() # pylint: disable=no-member
+ except Exception:
+ watcher = asyncio.SafeChildWatcher()
+ loop = policy.get_event_loop()
+
+ logging.debug(
+ "event_loop_fixture: attaching new watcher %s to loop and setting in policy",
+ watcher,
+ )
+ watcher.attach_loop(loop)
+ policy.set_child_watcher(watcher)
+ policy.set_event_loop(loop)
+ assert asyncio.get_event_loop_policy().get_child_watcher() is watcher
+
+ return loop
+
+
+class Commander: # pylint: disable=R0904
+ """An object that can execute commands."""
+
+ tmux_wait_gen = 0
+
+ def __init__(self, name, logger=None, unet=None, **kwargs):
+ """Create a Commander.
+
+ Args:
+ name: name of the commander object
+ logger: logger to use for logging commands a defualt is supplied if this
+ is None
+ unet: unet that owns this object, only used by Commander in run_in_window,
+ otherwise can be None.
+ """
+ # del kwargs # deal with lint warning
+ # logging.warning("Commander: name %s kwargs %s", name, kwargs)
+
+ self.name = name
+ self.unet = unet
+ self.deleting = False
+ self.last = None
+ self.exec_paths = {}
+
+ if not logger:
+ logname = f"munet.{self.__class__.__name__.lower()}.{name}"
+ self.logger = logging.getLogger(logname)
+ self.logger.setLevel(logging.DEBUG)
+ else:
+ self.logger = logger
+
+ super().__init__(**kwargs)
+
+ @property
+ def is_vm(self):
+ return False
+
+ @property
+ def is_container(self):
+ return False
+
+ def set_logger(self, logfile):
+ self.logger = logging.getLogger(__name__ + ".commander." + self.name)
+ self.logger.setLevel(logging.DEBUG)
+ if isinstance(logfile, str):
+ 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 _get_pre_cmd(self, use_str, use_pty, **kwargs):
+ """Get the pre-user-command values.
+
+ The values returned here should be what is required to cause the user's command
+ to execute in the correct context (e.g., namespace, container, sshremote).
+ """
+ del kwargs
+ del use_pty
+ return "" if use_str else []
+
+ def __str__(self):
+ return f"{self.__class__.__name__}({self.name})"
+
+ async def async_get_exec_path(self, binary):
+ """Return the full path to the binary executable.
+
+ `binary` :: binary name or list of binary names
+ """
+ return await _async_get_exec_path(
+ binary, self.async_cmd_status_nsonly, self.exec_paths
+ )
+
+ def get_exec_path(self, binary):
+ """Return the full path to the binary executable.
+
+ `binary` :: binary name or list of binary names
+ """
+ return _get_exec_path(binary, self.cmd_status_nsonly, self.exec_paths)
+
+ def get_exec_path_host(self, binary):
+ """Return the full path to the binary executable.
+
+ If the object is actually a derived class (e.g., a container) this method will
+ return the exec path for the native namespace rather than the container. The
+ path is the one which the other xxx_host methods will use.
+
+ `binary` :: binary name or list of binary names
+ """
+ return get_exec_path_host(binary)
+
+ def test(self, flags, arg):
+ """Run test binary, with flags and arg."""
+ test_path = self.get_exec_path(["test"])
+ rc, _, _ = self.cmd_status([test_path, flags, arg], warn=False)
+ return not rc
+
+ def test_nsonly(self, flags, arg):
+ """Run test binary, with flags and arg."""
+ test_path = self.get_exec_path(["test"])
+ rc, _, _ = self.cmd_status_nsonly([test_path, flags, arg], warn=False)
+ return not rc
+
+ def path_exists(self, path):
+ """Check if path exists."""
+ return self.test("-e", path)
+
+ async def cleanup_pid(self, pid, kill_pid=None):
+ """Signal a pid to exit with escalating forcefulness."""
+ if kill_pid is None:
+ kill_pid = pid
+
+ for sn in (signal.SIGHUP, signal.SIGKILL):
+ self.logger.debug(
+ "%s: %s %s (wait %s)", self, signal.Signals(sn).name, kill_pid, pid
+ )
+
+ os.kill(kill_pid, sn)
+
+ # No need to wait after this.
+ if sn == signal.SIGKILL:
+ return
+
+ # try each signal, waiting 15 seconds for exit before advancing
+ wait_sec = 30
+ self.logger.debug("%s: waiting %ss for pid to exit", self, wait_sec)
+ for _ in Timeout(wait_sec):
+ try:
+ status = os.waitpid(pid, os.WNOHANG)
+ if status == (0, 0):
+ await asyncio.sleep(0.1)
+ else:
+ self.logger.debug("pid %s exited status %s", pid, status)
+ return
+ except OSError as error:
+ if error.errno == errno.ECHILD:
+ self.logger.debug("%s: pid %s was reaped", self, pid)
+ else:
+ self.logger.warning(
+ "%s: error waiting on pid %s: %s", self, pid, error
+ )
+ return
+ self.logger.debug("%s: timeout waiting on pid %s to exit", self, pid)
+
+ def _get_sub_args(self, cmd_list, defaults, use_pty=False, ns_only=False, **kwargs):
+ """Returns pre-command, cmd, and default keyword args."""
+ assert not isinstance(cmd_list, str)
+
+ defaults["shell"] = False
+ pre_cmd_list = self._get_pre_cmd(False, use_pty, ns_only=ns_only, **kwargs)
+ cmd_list = [str(x) for x in cmd_list]
+
+ # os_env = {k: v for k, v in os.environ.items() if k.startswith("MUNET")}
+ # env = {**os_env, **(kwargs["env"] if "env" in kwargs else {})}
+ env = {**(kwargs["env"] if "env" in kwargs else os.environ)}
+ if "MUNET_NODENAME" not in env:
+ env["MUNET_NODENAME"] = self.name
+ kwargs["env"] = env
+
+ defaults.update(kwargs)
+
+ return pre_cmd_list, cmd_list, defaults
+
+ def _common_prologue(self, async_exec, method, cmd, skip_pre_cmd=False, **kwargs):
+ cmd_list = self._get_cmd_as_list(cmd)
+ if method == "_spawn":
+ defaults = {
+ "encoding": "utf-8",
+ "codec_errors": "ignore",
+ }
+ else:
+ defaults = {
+ "stdout": subprocess.PIPE,
+ "stderr": subprocess.PIPE,
+ }
+ if not async_exec:
+ defaults["encoding"] = "utf-8"
+
+ pre_cmd_list, cmd_list, defaults = self._get_sub_args(
+ cmd_list, defaults, **kwargs
+ )
+
+ use_pty = kwargs.get("use_pty", False)
+ if method == "_spawn":
+ # spawn doesn't take "shell" keyword arg
+ if "shell" in defaults:
+ del defaults["shell"]
+ # this is required to avoid receiving a STOPPED signal on expect!
+ if not use_pty:
+ defaults["preexec_fn"] = os.setsid
+ defaults["env"]["PS1"] = "$ "
+
+ if not detailed_cmd_logging:
+ pre_cmd_str = shlex.join(pre_cmd_list) if not skip_pre_cmd else ""
+ if "nsenter" in pre_cmd_str:
+ self.logger.debug('%s("%s")', method, shlex.join(cmd_list))
+ elif pre_cmd_str:
+ self.logger.debug(
+ '%s("%s") [precmd: %s]', method, shlex.join(cmd_list), pre_cmd_str
+ )
+ else:
+ self.logger.debug('%s("%s") [no precmd]', method, shlex.join(cmd_list))
+ else:
+ self.logger.debug(
+ '%s: %s %s("%s", pre_cmd: "%s" use_pty: %s kwargs: %.120s)',
+ self,
+ "XXX" if method == "_spawn" else "",
+ method,
+ cmd_list,
+ pre_cmd_list if not skip_pre_cmd else "",
+ use_pty,
+ defaults,
+ )
+
+ actual_cmd_list = cmd_list if skip_pre_cmd else pre_cmd_list + cmd_list
+ return actual_cmd_list, defaults
+
+ async def _async_popen(self, method, cmd, **kwargs):
+ """Create a new asynchronous subprocess."""
+ acmd, kwargs = self._common_prologue(True, method, cmd, **kwargs)
+ p = await asyncio.create_subprocess_exec(*acmd, **kwargs)
+ return p, acmd
+
+ def _popen(self, method, cmd, **kwargs):
+ """Create a subprocess."""
+ acmd, kwargs = self._common_prologue(False, method, cmd, **kwargs)
+ p = subprocess.Popen(acmd, **kwargs)
+ return p, acmd
+
+ def _fdspawn(self, fo, **kwargs):
+ defaults = {}
+ defaults.update(kwargs)
+
+ if "echo" in defaults:
+ del defaults["echo"]
+
+ if "encoding" not in defaults:
+ defaults["encoding"] = "utf-8"
+ if "codec_errors" not in defaults:
+ defaults["codec_errors"] = "ignore"
+ encoding = defaults["encoding"]
+
+ self.logger.debug("%s: _fdspawn(%s, kwargs: %s)", self, fo, defaults)
+
+ p = fdspawn(fo, **defaults)
+
+ # We don't have TTY like conversions of LF to CRLF
+ p.crlf = os.linesep.encode(encoding)
+
+ # we own the socket now detach the file descriptor to keep it from closing
+ if hasattr(fo, "detach"):
+ fo.detach()
+
+ return p
+
+ def _spawn(self, cmd, skip_pre_cmd=False, use_pty=False, echo=False, **kwargs):
+ logging.debug(
+ '%s: XXX _spawn: cmd "%s" skip_pre_cmd %s use_pty %s echo %s kwargs %s',
+ self,
+ cmd,
+ skip_pre_cmd,
+ use_pty,
+ echo,
+ kwargs,
+ )
+ actual_cmd, defaults = self._common_prologue(
+ False, "_spawn", cmd, skip_pre_cmd=skip_pre_cmd, use_pty=use_pty, **kwargs
+ )
+
+ self.logger.debug(
+ '%s: XXX %s("%s", use_pty %s echo %s defaults: %s)',
+ self,
+ "PopenSpawn" if not use_pty else "pexpect.spawn",
+ actual_cmd,
+ use_pty,
+ echo,
+ defaults,
+ )
+
+ # We don't specify a timeout it defaults to 30s is that OK?
+ if not use_pty:
+ p = PopenSpawn(actual_cmd, **defaults)
+ else:
+ p = pexpect.spawn(actual_cmd[0], actual_cmd[1:], echo=echo, **defaults)
+ return p, actual_cmd
+
+ def spawn(
+ self,
+ cmd,
+ spawned_re,
+ expects=(),
+ sends=(),
+ use_pty=False,
+ logfile=None,
+ logfile_read=None,
+ logfile_send=None,
+ trace=None,
+ **kwargs,
+ ):
+ """Create a spawned send/expect process.
+
+ Args:
+ cmd: list of args to exec/popen with, or an already open socket
+ spawned_re: what to look for to know when done, `spawn` returns when seen
+ expects: a list of regex other than `spawned_re` to look for. Commonly,
+ "ogin:" or "[Pp]assword:"r.
+ sends: what to send when an element of `expects` matches. So e.g., the
+ username or password if thats what corresponding expect matched. Can
+ be the empty string to send nothing.
+ use_pty: true for pty based expect, otherwise uses popen (pipes/files)
+ trace: if true then log send/expects
+ **kwargs - kwargs passed on the _spawn.
+
+ Returns:
+ A pexpect process.
+
+ Raises:
+ pexpect.TIMEOUT, pexpect.EOF as documented in `pexpect`
+ CalledProcessError if EOF is seen and `cmd` exited then
+ raises a CalledProcessError to indicate the failure.
+ """
+ if is_file_like(cmd):
+ assert not use_pty
+ ac = "*socket*"
+ p = self._fdspawn(cmd, **kwargs)
+ else:
+ p, ac = self._spawn(cmd, use_pty=use_pty, **kwargs)
+
+ if logfile:
+ p.logfile = logfile
+ if logfile_read:
+ p.logfile_read = logfile_read
+ if logfile_send:
+ p.logfile_send = logfile_send
+
+ # for spawned shells (i.e., a direct command an not a console)
+ # this is wrong and will cause 2 prompts
+ if not use_pty:
+ # This isn't very nice looking
+ p.echo = False
+ if not is_file_like(cmd):
+ p.isalive = lambda: p.proc.poll() is None
+ if not hasattr(p, "close"):
+ p.close = p.wait
+
+ # Do a quick check to see if we got the prompt right away, otherwise we may be
+ # at a console so we send a \n to re-issue the prompt
+ index = p.expect([spawned_re, pexpect.TIMEOUT, pexpect.EOF], timeout=0.1)
+ if index == 0:
+ assert p.match is not None
+ self.logger.debug(
+ "%s: got spawned_re quick: '%s' matching '%s'",
+ self,
+ p.match.group(0),
+ spawned_re,
+ )
+ return p
+
+ # Now send a CRLF to cause the prompt (or whatever else) to re-issue
+ p.send("\n")
+ try:
+ patterns = [spawned_re, *expects]
+
+ self.logger.debug("%s: expecting: %s", self, patterns)
+
+ while index := p.expect(patterns):
+ if trace:
+ assert p.match is not None
+ self.logger.debug(
+ "%s: got expect: '%s' matching %d '%s', sending '%s'",
+ self,
+ p.match.group(0),
+ index,
+ patterns[index],
+ sends[index - 1],
+ )
+ if sends[index - 1]:
+ p.send(sends[index - 1])
+
+ self.logger.debug("%s: expecting again: %s", self, patterns)
+ self.logger.debug(
+ "%s: got spawned_re: '%s' matching '%s'",
+ self,
+ p.match.group(0),
+ spawned_re,
+ )
+ return p
+ except pexpect.TIMEOUT:
+ self.logger.error(
+ "%s: TIMEOUT looking for spawned_re '%s' expect buffer so far:\n%s",
+ self,
+ spawned_re,
+ indent(p.buffer),
+ )
+ raise
+ except pexpect.EOF as eoferr:
+ if p.isalive():
+ raise
+ rc = p.status
+ before = indent(p.before)
+ error = CalledProcessError(rc, ac, output=before)
+ self.logger.error(
+ "%s: EOF looking for spawned_re '%s' before EOF:\n%s",
+ self,
+ spawned_re,
+ before,
+ )
+ p.close()
+ raise error from eoferr
+
+ async def shell_spawn(
+ self,
+ cmd,
+ prompt,
+ expects=(),
+ sends=(),
+ use_pty=False,
+ will_echo=False,
+ is_bourne=True,
+ init_newline=False,
+ **kwargs,
+ ):
+ """Create a shell REPL (read-eval-print-loop).
+
+ Args:
+ cmd: shell and list of args to popen with, or an already open socket
+ prompt: the REPL prompt to look for, the function returns when seen
+ expects: a list of regex other than `spawned_re` to look for. Commonly,
+ "ogin:" or "[Pp]assword:"r.
+ sends: what to send when an element of `expects` matches. So e.g., the
+ username or password if thats what corresponding expect matched. Can
+ be the empty string to send nothing.
+ is_bourne: if False then do not modify shell prompt for internal
+ parser friently format, and do not expect continuation prompts.
+ init_newline: send an initial newline for non-bourne shell spawns, otherwise
+ expect the prompt simply from running the command
+ use_pty: true for pty based expect, otherwise uses popen (pipes/files)
+ will_echo: bash is buggy in that it echo's to non-tty unlike any other
+ sh/ksh, set this value to true if running back
+ **kwargs - kwargs passed on the _spawn.
+ """
+ combined_prompt = r"({}|{})".format(re.escape(PEXPECT_PROMPT), prompt)
+
+ assert not is_file_like(cmd) or not use_pty
+ p = self.spawn(
+ cmd,
+ combined_prompt,
+ expects=expects,
+ sends=sends,
+ use_pty=use_pty,
+ echo=False,
+ **kwargs,
+ )
+ assert not p.echo
+
+ if not is_bourne:
+ if init_newline:
+ p.send("\n")
+ return ShellWrapper(p, prompt, will_echo=will_echo)
+
+ ps1 = PEXPECT_PROMPT
+ ps2 = PEXPECT_CONTINUATION_PROMPT
+
+ # Avoid problems when =/usr/bin/env= prints the values
+ ps1p = ps1[:5] + "${UNSET_V}" + ps1[5:]
+ ps2p = ps2[:5] + "${UNSET_V}" + ps2[5:]
+
+ ps1 = re.escape(ps1)
+ ps2 = re.escape(ps2)
+
+ extra = "PAGER=cat; export PAGER; TERM=dumb; unset HISTFILE; set +o emacs +o vi"
+ pchg = "PS1='{0}' PS2='{1}' PROMPT_COMMAND=''\n".format(ps1p, ps2p)
+ p.send(pchg)
+ return ShellWrapper(p, ps1, ps2, extra_init_cmd=extra, will_echo=will_echo)
+
+ def popen(self, cmd, **kwargs):
+ """Creates a pipe with the given `command`.
+
+ Args:
+ cmd: `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 `bash -c`, otherwise `command` is a list and
+ will be invoked without a shell.
+
+ Returns:
+ a subprocess.Popen object.
+ """
+ return self._popen("popen", cmd, **kwargs)[0]
+
+ def popen_nsonly(self, cmd, **kwargs):
+ """Creates a pipe with the given `command`.
+
+ Args:
+ cmd: `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 `bash -c`, otherwise `command` is a list and
+ will be invoked without a shell.
+
+ Returns:
+ a subprocess.Popen object.
+ """
+ return self._popen("popen_nsonly", cmd, ns_only=True, **kwargs)[0]
+
+ async def async_popen(self, cmd, **kwargs):
+ """Creates a pipe with the given `command`.
+
+ Args:
+ cmd: `str` or `list` of command to open a pipe with.
+ **kwargs: kwargs is eventually passed on to create_subprocess_exec. If
+ `command` is a string then will be invoked with `bash -c`, otherwise
+ `command` is a list and will be invoked without a shell.
+
+ Returns:
+ a asyncio.subprocess.Process object.
+ """
+ p, _ = await self._async_popen("async_popen", cmd, **kwargs)
+ return p
+
+ async def async_popen_nsonly(self, cmd, **kwargs):
+ """Creates a pipe with the given `command`.
+
+ Args:
+ cmd: `str` or `list` of command to open a pipe with.
+ **kwargs: kwargs is eventually passed on to create_subprocess_exec. If
+ `command` is a string then will be invoked with `bash -c`, otherwise
+ `command` is a list and will be invoked without a shell.
+
+ Returns:
+ a asyncio.subprocess.Process object.
+ """
+ p, _ = await self._async_popen(
+ "async_popen_nsonly", cmd, ns_only=True, **kwargs
+ )
+ return p
+
+ async def async_cleanup_proc(self, p, pid=None):
+ """Terminate a process started with a popen call.
+
+ Args:
+ p: return value from :py:`async_popen`, :py:`popen`, et al.
+ pid: pid to signal instead of p.pid, typically a child of
+ cmd_p == nsenter.
+
+ Returns:
+ None on success, the ``p`` if multiple timeouts occur even
+ after a SIGKILL sent.
+ """
+ if not p:
+ return None
+
+ if p.returncode is not None:
+ if isinstance(p, subprocess.Popen):
+ o, e = p.communicate()
+ else:
+ o, e = await p.communicate()
+ self.logger.debug(
+ "%s: cmd_p already exited status: %s", self, proc_error(p, o, e)
+ )
+ return None
+
+ if pid is None:
+ pid = p.pid
+
+ self.logger.debug("%s: terminate process: %s (pid %s)", self, proc_str(p), pid)
+ try:
+ # This will SIGHUP and wait a while then SIGKILL and return immediately
+ await self.cleanup_pid(p.pid, pid)
+
+ # Wait another 2 seconds after the possible SIGKILL above for the
+ # parent nsenter to cleanup and exit
+ wait_secs = 2
+ if isinstance(p, subprocess.Popen):
+ o, e = p.communicate(timeout=wait_secs)
+ else:
+ o, e = await asyncio.wait_for(p.communicate(), timeout=wait_secs)
+ self.logger.debug(
+ "%s: cmd_p exited after kill, status: %s", self, proc_error(p, o, e)
+ )
+ except (asyncio.TimeoutError, subprocess.TimeoutExpired):
+ self.logger.warning("%s: SIGKILL timeout", self)
+ return p
+ except Exception as error:
+ self.logger.warning(
+ "%s: kill unexpected exception: %s", self, error, exc_info=True
+ )
+ return p
+ return None
+
+ @staticmethod
+ def _cmd_status_input(stdin):
+ pinput = None
+ if isinstance(stdin, (bytes, str)):
+ pinput = stdin
+ stdin = subprocess.PIPE
+ return pinput, stdin
+
+ def _cmd_status_finish(self, p, c, ac, o, e, raises, warn):
+ rc = p.returncode
+ self.last = (rc, ac, c, o, e)
+ if not rc:
+ resstr = comm_result(rc, o, e)
+ if resstr:
+ self.logger.debug("%s", resstr)
+ else:
+ if warn:
+ self.logger.warning("%s: proc failed: %s", self, proc_error(p, o, e))
+ if raises:
+ # error = Exception("stderr: {}".format(stderr))
+ # This annoyingly doesnt' show stderr when printed normally
+ raise CalledProcessError(rc, ac, o, e)
+ return rc, o, e
+
+ def _cmd_status(self, cmds, raises=False, warn=True, stdin=None, **kwargs):
+ """Execute a command."""
+ pinput, stdin = Commander._cmd_status_input(stdin)
+ p, actual_cmd = self._popen("cmd_status", cmds, stdin=stdin, **kwargs)
+ o, e = p.communicate(pinput)
+ return self._cmd_status_finish(p, cmds, actual_cmd, o, e, raises, warn)
+
+ async def _async_cmd_status(
+ self, cmds, raises=False, warn=True, stdin=None, text=None, **kwargs
+ ):
+ """Execute a command."""
+ pinput, stdin = Commander._cmd_status_input(stdin)
+ p, actual_cmd = await self._async_popen(
+ "async_cmd_status", cmds, stdin=stdin, **kwargs
+ )
+
+ if text is False:
+ encoding = None
+ else:
+ encoding = kwargs.get("encoding", "utf-8")
+
+ if encoding is not None and isinstance(pinput, str):
+ pinput = pinput.encode(encoding)
+ o, e = await p.communicate(pinput)
+ if encoding is not None:
+ o = o.decode(encoding) if o is not None else o
+ e = e.decode(encoding) if e is not None else e
+ return self._cmd_status_finish(p, cmds, actual_cmd, o, e, raises, warn)
+
+ def _get_cmd_as_list(self, cmd):
+ """Given a list or string return a list form for execution.
+
+ If `cmd` is a string then the returned list uses bash and looks
+ like this: ["/bin/bash", "-c", cmd]. Some node types override
+ this function if they utilize a different shell as to return
+ a different list of values.
+
+ Args:
+ cmd: list or string representing the command to execute.
+
+ Returns:
+ list of commands to execute.
+ """
+ if not isinstance(cmd, str):
+ cmds = cmd
+ else:
+ # Make sure the code doesn't think `cd` will work.
+ assert not re.match(r"cd(\s*|\s+(\S+))$", cmd)
+ cmds = ["/bin/bash", "-c", cmd]
+ return cmds
+
+ def cmd_nostatus(self, cmd, **kwargs):
+ """Run given command returning output[s].
+
+ Args:
+ cmd: `str` or `list` of the command to execute. If a string is given
+ it is run using a shell, otherwise the list is executed directly
+ as the binary and arguments.
+ **kwargs: kwargs is eventually passed on to Popen. If `command` is a string
+ then will be invoked with `bash -c`, otherwise `command` is a list and
+ will be invoked without a shell.
+
+ Returns:
+ if "stderr" is in kwargs and not equal to subprocess.STDOUT, then
+ both stdout and stderr are returned, otherwise stderr is combined
+ with stdout and only stdout is returned.
+ """
+ #
+ # This method serves as the basis for all derived sync cmd variations, so to
+ # override sync cmd behavior simply override this function and *not* the other
+ # variations, unless you are changing only that variation's behavior
+ #
+
+ # XXX change this back to _cmd_status instead of cmd_status when we
+ # consolidate and cleanup the container overrides of *cmd_* functions
+
+ cmds = cmd
+ if "stderr" in kwargs and kwargs["stderr"] != subprocess.STDOUT:
+ _, o, e = self.cmd_status(cmds, **kwargs)
+ return o, e
+ if "stderr" in kwargs:
+ del kwargs["stderr"]
+ _, o, _ = self.cmd_status(cmds, stderr=subprocess.STDOUT, **kwargs)
+ return o
+
+ def cmd_status(self, cmd, **kwargs):
+ """Run given command returning status and outputs.
+
+ Args:
+ cmd: `str` or `list` of the command to execute. If a string is given
+ it is run using a shell, otherwise the list is executed directly
+ as the binary and arguments.
+ **kwargs: kwargs is eventually passed on to Popen. If `command` is a string
+ then will be invoked with `bash -c`, otherwise `command` is a list and
+ will be invoked without a shell.
+
+ Returns:
+ (status, output, error) are returned
+ status: the returncode of the command.
+ output: stdout as a string from the command.
+ error: stderr as a string from the command.
+ """
+ #
+ # This method serves as the basis for all derived sync cmd variations, so to
+ # override sync cmd behavior simply override this function and *not* the other
+ # variations, unless you are changing only that variation's behavior
+ #
+ return self._cmd_status(cmd, **kwargs)
+
+ def cmd_raises(self, cmd, **kwargs):
+ """Execute a command. Raise an exception on errors.
+
+ Args:
+ cmd: `str` or `list` of the command to execute. If a string is given
+ it is run using a shell, otherwise the list is executed directly
+ as the binary and arguments.
+ **kwargs: kwargs is eventually passed on to Popen. If `command` is a string
+ then will be invoked with `bash -c`, otherwise `command` is a list and
+ will be invoked without a shell.
+
+ Returns:
+ output: stdout as a string from the command.
+
+ Raises:
+ CalledProcessError: on non-zero exit status
+ """
+ _, stdout, _ = self._cmd_status(cmd, raises=True, **kwargs)
+ return stdout
+
+ def cmd_nostatus_nsonly(self, cmd, **kwargs):
+ # Make sure the command runs on the host and not in any container.
+ return self.cmd_nostatus(cmd, ns_only=True, **kwargs)
+
+ def cmd_status_nsonly(self, cmd, **kwargs):
+ # Make sure the command runs on the host and not in any container.
+ return self._cmd_status(cmd, ns_only=True, **kwargs)
+
+ def cmd_raises_nsonly(self, cmd, **kwargs):
+ # Make sure the command runs on the host and not in any container.
+ _, stdout, _ = self._cmd_status(cmd, raises=True, ns_only=True, **kwargs)
+ return stdout
+
+ async def async_cmd_status(self, cmd, **kwargs):
+ """Run given command returning status and outputs.
+
+ Args:
+ cmd: `str` or `list` of the command to execute. If a string is given
+ it is run using a shell, otherwise the list is executed directly
+ as the binary and arguments.
+ **kwargs: kwargs is eventually passed on to create_subprocess_exec. If
+ `cmd` is a string then will be invoked with `bash -c`, otherwise
+ `cmd` is a list and will be invoked without a shell.
+
+ Returns:
+ (status, output, error) are returned
+ status: the returncode of the command.
+ output: stdout as a string from the command.
+ error: stderr as a string from the command.
+ """
+ #
+ # This method serves as the basis for all derived async cmd variations, so to
+ # override async cmd behavior simply override this function and *not* the other
+ # variations, unless you are changing only that variation's behavior
+ #
+ return await self._async_cmd_status(cmd, **kwargs)
+
+ async def async_cmd_nostatus(self, cmd, **kwargs):
+ """Run given command returning output[s].
+
+ Args:
+ cmd: `str` or `list` of the command to execute. If a string is given
+ it is run using a shell, otherwise the list is executed directly
+ as the binary and arguments.
+ **kwargs: kwargs is eventually passed on to create_subprocess_exec. If
+ `cmd` is a string then will be invoked with `bash -c`, otherwise
+ `cmd` is a list and will be invoked without a shell.
+
+ Returns:
+ if "stderr" is in kwargs and not equal to subprocess.STDOUT, then
+ both stdout and stderr are returned, otherwise stderr is combined
+ with stdout and only stdout is returned.
+
+ """
+ # XXX change this back to _async_cmd_status instead of cmd_status when we
+ # consolidate and cleanup the container overrides of *cmd_* functions
+
+ cmds = cmd
+ if "stderr" in kwargs and kwargs["stderr"] != subprocess.STDOUT:
+ _, o, e = await self._async_cmd_status(cmds, **kwargs)
+ return o, e
+ if "stderr" in kwargs:
+ del kwargs["stderr"]
+ _, o, _ = await self._async_cmd_status(cmds, stderr=subprocess.STDOUT, **kwargs)
+ return o
+
+ async def async_cmd_raises(self, cmd, **kwargs):
+ """Execute a command. Raise an exception on errors.
+
+ Args:
+ cmd: `str` or `list` of the command to execute. If a string is given
+ it is run using a shell, otherwise the list is executed directly
+ as the binary and arguments.
+ **kwargs: kwargs is eventually passed on to create_subprocess_exec. If
+ `cmd` is a string then will be invoked with `bash -c`, otherwise
+ `cmd` is a list and will be invoked without a shell.
+
+ Returns:
+ output: stdout as a string from the command.
+
+ Raises:
+ CalledProcessError: on non-zero exit status
+ """
+ _, stdout, _ = await self._async_cmd_status(cmd, raises=True, **kwargs)
+ return stdout
+
+ async def async_cmd_status_nsonly(self, cmd, **kwargs):
+ # Make sure the command runs on the host and not in any container.
+ return await self._async_cmd_status(cmd, ns_only=True, **kwargs)
+
+ async def async_cmd_raises_nsonly(self, cmd, **kwargs):
+ # Make sure the command runs on the host and not in any container.
+ _, stdout, _ = await self._async_cmd_status(
+ cmd, raises=True, ns_only=True, **kwargs
+ )
+ return stdout
+
+ 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
+
+ # 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,
+ ns_only=False,
+ ):
+ """Run a command in a new window (TMUX, Screen or XTerm).
+
+ Args:
+ cmd: string to execute.
+ 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 isinstance(wait_for, str):
+ channel = wait_for
+ elif wait_for is True:
+ channel = "{}-wait-{}".format(our_pid, Commander.tmux_wait_gen)
+ Commander.tmux_wait_gen += 1
+
+ if forcex or ("TMUX" not in os.environ and "STY" not in os.environ):
+ root_level = False
+ else:
+ root_level = True
+
+ # SUDO: The important thing to note is that with all these methods we are
+ # executing on the users windowing system, so even though we are normally
+ # running as root, we will not be when the command is dispatched. Also
+ # in the case of SCREEN and X11 we need to sudo *back* to the user as well
+ # This is also done by SSHRemote by defualt so we should *not* sudo back
+ # if we are SSHRemote.
+
+ # XXX need to test ssh in screen
+ # XXX need to test ssh in Xterm
+ sudo_path = get_exec_path_host(["sudo"])
+ # This first test case seems same as last but using list instead of string?
+ if self.is_vm and self.use_ssh: # pylint: disable=E1101
+ if isinstance(cmd, str):
+ cmd = shlex.split(cmd)
+ cmd = ["/usr/bin/env", f"MUNET_NODENAME={self.name}"] + cmd
+
+ # get the ssh cmd
+ cmd = self._get_pre_cmd(False, True, ns_only=ns_only) + [shlex.join(cmd)]
+ unet = self.unet # pylint: disable=E1101
+ uns_cmd = unet._get_pre_cmd( # pylint: disable=W0212
+ False, True, ns_only=True, root_level=root_level
+ )
+ # get the nsenter for munet
+ nscmd = [
+ sudo_path,
+ *uns_cmd,
+ *cmd,
+ ]
+ else:
+ # This is the command to execute to be inside the namespace.
+ # We are getting into trouble with quoting.
+ # Why aren't we passing in MUNET_RUNDIR?
+ cmd = f"/usr/bin/env MUNET_NODENAME={self.name} {cmd}"
+ # We need sudo b/c we are executing as the user inside the window system.
+ sudo_path = get_exec_path_host(["sudo"])
+ nscmd = (
+ sudo_path
+ + " "
+ + self._get_pre_cmd(True, True, ns_only=ns_only, root_level=root_level)
+ + " "
+ + cmd
+ )
+
+ if "TMUX" in os.environ and not forcex:
+ cmd = [get_exec_path_host("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)
+
+ # nscmd is always added as single string argument
+ if not isinstance(nscmd, str):
+ nscmd = shlex.join(nscmd)
+ if title:
+ nscmd = f"printf '\033]2;{title}\033\\'; {nscmd}"
+ if channel:
+ nscmd = f'trap "tmux wait -S {channel}; exit 0" EXIT; {nscmd}'
+ cmd.append(nscmd)
+
+ elif "STY" in os.environ and not forcex:
+ # wait for not supported in screen for now
+ channel = None
+ cmd = [get_exec_path_host("screen")]
+ if not os.path.exists(
+ "/run/screen/S-{}/{}".format(os.environ["USER"], os.environ["STY"])
+ ):
+ # XXX not appropriate for ssh
+ cmd = ["sudo", "-Eu", os.environ["SUDO_USER"]] + cmd
+
+ if title:
+ cmd.append("-t")
+ cmd.append(title)
+
+ if isinstance(nscmd, str):
+ nscmd = shlex.split(nscmd)
+ cmd.extend(nscmd)
+ elif "DISPLAY" in os.environ:
+ cmd = [get_exec_path_host("xterm")]
+ if "SUDO_USER" in os.environ:
+ # Do this b/c making things work as root with xauth seems hard
+ cmd = [
+ get_exec_path_host("sudo"),
+ "-Eu",
+ os.environ["SUDO_USER"],
+ ] + cmd
+ if title:
+ cmd.append("-T")
+ cmd.append(title)
+
+ cmd.append("-e")
+ if isinstance(nscmd, str):
+ cmd.extend(shlex.split(nscmd))
+ else:
+ cmd.extend(nscmd)
+
+ # if channel:
+ # return self.cmd_raises(cmd, skip_pre_cmd=True)
+ # else:
+ p = commander.popen(
+ cmd,
+ # skip_pre_cmd=True,
+ stdin=None,
+ shell=False,
+ )
+ # We should reap the child and report the error then.
+ 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, ns_only=True).strip()
+ # We are prepending the nsenter command, so use unet.rootcmd
+ pane_info = commander.cmd_raises(cmd).strip()
+
+ # Re-adjust the layout
+ if "TMUX" in os.environ:
+ cmd = [
+ get_exec_path_host("tmux"),
+ "select-layout",
+ "-t",
+ pane_info if not tmux_target else tmux_target,
+ "tiled",
+ ]
+ commander.cmd_status(cmd)
+
+ # Wait here if we weren't handed the channel to wait for
+ if channel and wait_for is True:
+ cmd = [get_exec_path_host("tmux"), "wait", channel]
+ # commander.cmd_status(cmd, skip_pre_cmd=True)
+ commander.cmd_status(cmd)
+
+ return pane_info
+
+ def delete(self):
+ """Calls self.async_delete within an exec loop."""
+ asyncio.run(self.async_delete())
+
+ async def _async_delete(self):
+ """Delete this objects resources.
+
+ This is the actual implementation of the resource cleanup, each class
+ should cleanup it's own resources, generally catching and reporting,
+ but not reraising any exceptions for it's own cleanup, then it should
+ invoke `super()._async_delete() without catching any exceptions raised
+ therein. See other examples in `base.py` or `native.py`
+ """
+ self.logger.info("%s: deleted", self)
+
+ async def async_delete(self):
+ """Delete the Commander (or derived object).
+
+ The actual implementation for any class should be in `_async_delete`
+ new derived classes should look at the documentation for that function.
+ """
+ try:
+ self.deleting = True
+ await self._async_delete()
+ except Exception as error:
+ self.logger.error("%s: error while deleting: %s", self, error)
+
+
+class InterfaceMixin:
+ """A mixin class to support interface functionality."""
+
+ def __init__(self, *args, **kwargs):
+ # del kwargs # get rid of lint
+ # logging.warning("InterfaceMixin: args: %s kwargs: %s", args, kwargs)
+
+ self._intf_addrs = defaultdict(lambda: [None, None])
+ self.net_intfs = {}
+ self.next_intf_index = 0
+ self.basename = "eth"
+ # self.basename = name + "-eth"
+ super().__init__(*args, **kwargs)
+
+ @property
+ def intfs(self):
+ return sorted(self._intf_addrs.keys())
+
+ @property
+ def networks(self):
+ return sorted(self.net_intfs.keys())
+
+ def get_intf_addr(self, ifname, ipv6=False):
+ if ifname not in self._intf_addrs:
+ return None
+ return self._intf_addrs[ifname][bool(ipv6)]
+
+ def set_intf_addr(self, ifname, ifaddr):
+ ifaddr = ipaddress.ip_interface(ifaddr)
+ self._intf_addrs[ifname][ifaddr.version == 6] = ifaddr
+
+ def net_addr(self, netname, ipv6=False):
+ if netname not in self.net_intfs:
+ return None
+ return self.get_intf_addr(self.net_intfs[netname], ipv6=ipv6)
+
+ def set_intf_basename(self, basename):
+ self.basename = basename
+
+ def get_next_intf_name(self):
+ while True:
+ ifname = self.basename + str(self.next_intf_index)
+ self.next_intf_index += 1
+ if ifname not in self._intf_addrs:
+ break
+ return ifname
+
+ def get_ns_ifname(self, ifname):
+ """Return a namespace unique interface name.
+
+ This function is primarily overriden by L3QemuVM, IOW by any class
+ that doesn't create it's own network namespace and will share that
+ with the root (unet) namespace.
+
+ Args:
+ ifname: the interface name.
+
+ Returns:
+ A name unique to the namespace of this object. By defualt the assumption
+ is the ifname is namespace unique.
+ """
+ return ifname
+
+ def register_interface(self, ifname):
+ if ifname not in self._intf_addrs:
+ self._intf_addrs[ifname] = [None, None]
+
+ def register_network(self, netname, ifname):
+ if netname in self.net_intfs:
+ assert self.net_intfs[netname] == ifname
+ else:
+ self.net_intfs[netname] = ifname
+
+ def get_linux_tc_args(self, ifname, config):
+ """Get interface constraints (jitter, delay, rate) for linux TC.
+
+ The keys and their values are as follows:
+
+ delay (int): number of microseconds
+ jitter (int): number of microseconds
+ jitter-correlation (float): % correlation to previous (default 10%)
+ loss (float): % of loss
+ loss-correlation (float): % correlation to previous (default 0%)
+ rate (int or str): bits per second, string allows for use of
+ {KMGTKiMiGiTi} prefixes "i" means K == 1024 otherwise K == 1000
+ """
+ del ifname # unused
+
+ netem_args = ""
+
+ def get_number(c, v, d=None):
+ if v not in c or c[v] is None:
+ return d
+ return convert_number(c[v])
+
+ delay = get_number(config, "delay")
+ if delay is not None:
+ netem_args += f" delay {delay}usec"
+
+ jitter = get_number(config, "jitter")
+ if jitter is not None:
+ if not delay:
+ raise ValueError("jitter but no delay specified")
+ jitter_correlation = get_number(config, "jitter-correlation", 10)
+ netem_args += f" {jitter}usec {jitter_correlation}%"
+
+ loss = get_number(config, "loss")
+ if loss is not None:
+ loss_correlation = get_number(config, "loss-correlation", 0)
+ if loss_correlation:
+ netem_args += f" loss {loss}% {loss_correlation}%"
+ else:
+ netem_args += f" loss {loss}%"
+
+ if (o_rate := config.get("rate")) is None:
+ return netem_args, ""
+
+ #
+ # This comment is not correct, but is trying to talk through/learn the
+ # machinery.
+ #
+ # tokens arrive at `rate` into token buffer.
+ # limit - number of bytes that can be queued waiting for tokens
+ # -or-
+ # latency - maximum amount of time a packet may sit in TBF queue
+ #
+ # So this just allows receiving faster than rate for latency amount of
+ # time, before dropping.
+ #
+ # latency = sizeofbucket(limit) / rate (peakrate?)
+ #
+ # 32kbit
+ # -------- = latency = 320ms
+ # 100kbps
+ #
+ # -but then-
+ # burst ([token] buffer) the largest number of instantaneous
+ # tokens available (i.e, bucket size).
+
+ tbf_args = ""
+ DEFLIMIT = 1518 * 1
+ DEFBURST = 1518 * 2
+ try:
+ tc_rate = o_rate["rate"]
+ tc_rate = convert_number(tc_rate)
+ limit = convert_number(o_rate.get("limit", DEFLIMIT))
+ burst = convert_number(o_rate.get("burst", DEFBURST))
+ except (KeyError, TypeError):
+ tc_rate = convert_number(o_rate)
+ limit = convert_number(DEFLIMIT)
+ burst = convert_number(DEFBURST)
+ tbf_args += f" rate {tc_rate/1000}kbit"
+ if delay:
+ # give an extra 1/10 of buffer space to handle delay
+ tbf_args += f" limit {limit} burst {burst}"
+ else:
+ tbf_args += f" limit {limit} burst {burst}"
+
+ return netem_args, tbf_args
+
+ def set_intf_constraints(self, ifname, **constraints):
+ """Set interface outbound constraints.
+
+ Set outbound constraints (jitter, delay, rate) for an interface. All arguments
+ may also be passed as a string and will be converted to numerical format. All
+ arguments are also optional. If not specified then that existing constraint will
+ be cleared.
+
+ Args:
+ ifname: the name of the interface
+ delay (int): number of microseconds.
+ jitter (int): number of microseconds.
+ jitter-correlation (float): Percent correlation to previous (default 10%).
+ loss (float): Percent of loss.
+ loss-correlation (float): Percent correlation to previous (default 25%).
+ rate (int): bits per second, string allows for use of
+ {KMGTKiMiGiTi} prefixes "i" means K == 1024 otherwise K == 1000.
+ """
+ nsifname = self.get_ns_ifname(ifname)
+ netem_args, tbf_args = self.get_linux_tc_args(nsifname, constraints)
+ count = 1
+ selector = f"root handle {count}:"
+ if netem_args:
+ self.cmd_raises(
+ f"tc qdisc add dev {nsifname} {selector} netem {netem_args}"
+ )
+ count += 1
+ selector = f"parent {count-1}: handle {count}"
+ # Place rate limit after delay otherwise limit/burst too complex
+ if tbf_args:
+ self.cmd_raises(f"tc qdisc add dev {nsifname} {selector} tbf {tbf_args}")
+
+ self.cmd_raises(f"tc qdisc show dev {nsifname}")
+
+
+class LinuxNamespace(Commander, InterfaceMixin):
+ """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,
+ unshare_inline=False,
+ set_hostname=True,
+ private_mounts=None,
+ **kwargs,
+ ):
+ """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`.
+ unshare_inline: Unshare the process itself rather than using a proxy.
+ logger: Passed to superclass.
+ """
+ # logging.warning("LinuxNamespace: name %s kwargs %s", name, kwargs)
+
+ super().__init__(name, **kwargs)
+
+ unet = self.unet
+
+ self.logger.debug("%s: creating", self)
+
+ self.cwd = os.path.abspath(os.getcwd())
+
+ self.nsflags = []
+ self.ifnetns = {}
+ self.uflags = 0
+ self.p_ns_fds = None
+ self.p_ns_fnames = None
+ self.pid_ns = False
+ self.init_pid = None
+ self.unshare_inline = unshare_inline
+ self.nsenter_fork = True
+
+ #
+ # Collect the namespaces to unshare
+ #
+ if hasattr(self, "proc_path") and self.proc_path: # pylint: disable=no-member
+ pp = Path(self.proc_path) # pylint: disable=no-member
+ else:
+ pp = unet.proc_path if unet else Path("/proc")
+ pp = pp.joinpath("%P%", "ns")
+
+ flags = ""
+ uflags = 0
+ nslist = []
+ nsflags = []
+ if cgroup:
+ nselm = "cgroup"
+ nslist.append(nselm)
+ nsflags.append(f"--{nselm}={pp / nselm}")
+ flags += "C"
+ uflags |= linux.CLONE_NEWCGROUP
+ if ipc:
+ nselm = "ipc"
+ nslist.append(nselm)
+ nsflags.append(f"--{nselm}={pp / nselm}")
+ flags += "i"
+ uflags |= linux.CLONE_NEWIPC
+ if mount or pid:
+ # We need a new mount namespace for pid
+ nselm = "mnt"
+ nslist.append(nselm)
+ nsflags.append(f"--mount={pp / nselm}")
+ mount = True
+ flags += "m"
+ uflags |= linux.CLONE_NEWNS
+ if net:
+ nselm = "net"
+ nslist.append(nselm)
+ nsflags.append(f"--{nselm}={pp / nselm}")
+ # if pid:
+ # os.system(f"touch /tmp/netns-{name}")
+ # cmd.append(f"--net=/tmp/netns-{name}")
+ # else:
+ flags += "n"
+ uflags |= linux.CLONE_NEWNET
+ if pid:
+ self.pid_ns = True
+ # We look for this b/c the unshare pid will share with /sibn/init
+ nselm = "pid_for_children"
+ nslist.append(nselm)
+ nsflags.append(f"--pid={pp / nselm}")
+ flags += "p"
+ uflags |= linux.CLONE_NEWPID
+ if time:
+ nselm = "time"
+ # XXX time_for_children?
+ nslist.append(nselm)
+ nsflags.append(f"--{nselm}={pp / nselm}")
+ flags += "T"
+ uflags |= linux.CLONE_NEWTIME
+ if user:
+ nselm = "user"
+ nslist.append(nselm)
+ nsflags.append(f"--{nselm}={pp / nselm}")
+ flags += "U"
+ uflags |= linux.CLONE_NEWUSER
+ if uts:
+ nselm = "uts"
+ nslist.append(nselm)
+ nsflags.append(f"--{nselm}={pp / nselm}")
+ flags += "u"
+ uflags |= linux.CLONE_NEWUTS
+
+ assert flags, "LinuxNamespace with no namespaces requested"
+
+ # Should look path up using resources maybe...
+ mutini_path = get_our_script_path("mutini")
+ if not mutini_path:
+ mutini_path = get_our_script_path("mutini.py")
+ assert mutini_path
+ cmd = [mutini_path, f"--unshare-flags={flags}", "-v"]
+ fname = fsafe_name(self.name) + "-mutini.log"
+ fname = (unet or self).rundir.joinpath(fname)
+ stdout = open(fname, "w", encoding="utf-8")
+ stderr = subprocess.STDOUT
+
+ #
+ # Save the current namespace info to compare against later
+ #
+
+ if not unet:
+ nsdict = {x: os.readlink(f"/proc/self/ns/{x}") for x in nslist}
+ else:
+ nsdict = {
+ x: os.readlink(f"{unet.proc_path}/{unet.pid}/ns/{x}") for x in nslist
+ }
+
+ #
+ # (A) Basically we need to save the pid of the unshare call for nsenter.
+ #
+ # For `unet is not None` (node case) the level this exists at is based on wether
+ # unet is using a forking nsenter or not. So if unet.nsenter_fork == True then
+ # we need the child pid of the p.pid (child of pid returned to us), otherwise
+ # unet.nsenter_fork == False and we just use p.pid as it will be unshare after
+ # nsenter exec's it.
+ #
+ # For the `unet is None` (unet case) the unshare is at the top level or
+ # non-existent so we always save the returned p.pid. If we are unshare_inline we
+ # won't have a __pre_cmd but we can save our child_pid to kill later, otherwise
+ # we set unet.pid to None b/c there's literally nothing to do.
+ #
+ # ---------------------------------------------------------------------------
+ # Breakdown for nested (non-unet) namespace creation, and what PID
+ # to use for __pre_cmd nsenter use.
+ # ---------------------------------------------------------------------------
+ #
+ # tl;dr
+ # - for non-inline unshare: Use BBB with pid_for_children, unless none/none
+ # #then (AAA) returned
+ # - for inline unshare: use returned pid (AAA) with pid_for_children
+ #
+ # All commands use unet.popen to launch the unshare of mutini or cat.
+ # mutini for PID unshare, otherwise cat. AAA is the returned pid BBB is the
+ # child of the returned.
+ #
+ # Unshare Variant
+ # ---------------
+ #
+ # Here we are running mutini if we are creating new pid namespace workspace,
+ # cat otherwise.
+ #
+ # [PID+PID] pid tree looks like this:
+ #
+ # PID NSPID PPID PGID
+ # uuu - N/A uuu main unet process
+ # AAA - uuu AAA nsenter (forking, from unet) (in unet namespaces -pid)
+ # BBB - AAA AAA unshare --fork --kill-child (forking)
+ # CCC 1 BBB CCC mutini (non-forking since it is pid 1 in new namespace)
+ #
+ # Use BBB if we use pid_for_children, CCC for all
+ #
+ # [PID+none] For non-pid workspace creation (but unet pid) we use cat and pid
+ # tree looks like this:
+ #
+ # PID PPID PGID
+ # uuu N/A uuu main unet process
+ # AAA uuu AAA nsenter (forking) (in unet namespaces -pid)
+ # BBB AAA AAA unshare -> cat (from unshare non-forking)
+ #
+ # Use BBB for all
+ #
+ # [none+PID] For pid workspace creation (but NOT unet pid) we use mutini and pid
+ # tree looks like this:
+ #
+ # PID NSPID PPID PGID
+ # uuu - N/A uuu main unet process
+ # AAA - uuu AAA nsenter -> unshare --fork --kill-child
+ # BBB 1 AAA AAA mutini (non-forking since it is pid 1 in new namespace)
+ #
+ # Use AAA if we use pid_for_children, BBB for all
+ #
+ # [none+none] For non-pid workspace and non-pid unet we use cat and pid tree
+ # looks like this:
+ #
+ # PID PPID PGID
+ # uuu N/A uuu main unet process
+ # AAA uuu AAA nsenter -> unshare -> cat
+ #
+ # Use AAA for all, there's no BBB
+ #
+ # Inline-Unshare Variant
+ # ----------------------
+ #
+ # For unshare_inline and new PID namespace we have unshared all but our PID
+ # namespace, but our children end up in the new namespace so the fork popen
+ # does is good enough.
+ #
+ # [PID+PID] pid tree looks like this:
+ #
+ # PID NSPID PPID PGID
+ # uuu - N/A uuu main unet process
+ # AAA - uuu AAA unshare --fork --kill-child (forking)
+ # BBB 1 AAA BBB mutini
+ #
+ # Use AAA if we use pid_for_children, BBB for all
+ #
+ # [PID+none] For non-pid workspace creation (but unet pid) we use cat and pid
+ # tree looks like this:
+ #
+ # PID PPID PGID
+ # uuu N/A uuu main unet process
+ # AAA uuu AAA unshare -> cat
+ #
+ # Use AAA for all
+ #
+ # [none+PID] For pid workspace creation (but NOT unet pid) we use mutini and pid
+ # tree looks like this:
+ #
+ # PID NSPID PPID PGID
+ # uuu - N/A uuu main unet process
+ # AAA - uuu AAA unshare --fork --kill-child
+ # BBB 1 AAA BBB mutini
+ #
+ # Use AAA if we use pid_for_children, BBB for all
+ #
+ # [none+none] For non-pid workspace and non-pid unet we use cat and pid tree
+ # looks like this:
+ #
+ # PID PPID PGID
+ # uuu N/A uuu main unet process
+ # AAA uuu AAA unshare -> cat
+ #
+ # Use AAA for all.
+ #
+ #
+ # ---------------------------------------------------------------------------
+ # Breakdown for unet namespace creation, and what PID to use for __pre_cmd
+ # ---------------------------------------------------------------------------
+ #
+ # tl;dr: save returned PID or nothing.
+ # - for non-inline unshare: Use AAA with pid_for_children (returned pid)
+ # - for inline unshare: no __precmd as the fork in popen is enough.
+ #
+ # Use commander to launch the unshare mutini/cat (for PID/none
+ # workspace PID) for non-inline case. AAA is the returned pid BBB is the child
+ # of the returned.
+ #
+ # Unshare Variant
+ # ---------------
+ #
+ # Here we are running mutini if we are creating new pid namespace workspace,
+ # cat otherwise.
+ #
+ # [PID] for unet pid creation pid tree looks like this:
+ #
+ # PID NSPID PPID PGID
+ # uuu - N/A uuu main unet process
+ # AAA - uuu AAA unshare --fork --kill-child (forking)
+ # BBB 1 AAA BBB mutini
+ #
+ # Use AAA if we use pid_for_children, BBB for all
+ #
+ # [none] for unet non-pid, pid tree looks like this:
+ #
+ # PID PPID PGID
+ # uuu N/A uuu main unet process
+ # AAA uuu AAA unshare -> cat
+ #
+ # Use AAA for all
+ #
+ # Inline-Unshare Variant
+ # -----------------------
+ #
+ # For unshare_inline and new PID namespace we have unshared all but our PID
+ # namespace, but our children end up in the new namespace so the fork in popen
+ # does is good enough.
+ #
+ # [PID] for unet pid creation pid tree looks like this:
+ #
+ # PID NSPID PPID PGID
+ # uuu - N/A uuu main unet process
+ # AAA 1 uuu AAA mutini
+ #
+ # Save p / p.pid, but don't configure any nsenter, uneeded.
+ #
+ # Use nothing as the fork when doing a popen is enough to be in all the right
+ # namepsaces.
+ #
+ # [none] for unet non-pid, pid tree looks like this:
+ #
+ # PID PPID PGID
+ # uuu N/A uuu main unet process
+ #
+ # Nothing, no __pre_cmd.
+ #
+ #
+
+ self.ppid = os.getppid()
+ self.unshare_inline = unshare_inline
+ if unshare_inline:
+ assert unet is None
+ self.uflags = uflags
+ #
+ # Open file descriptors for current namespaces for later resotration.
+ #
+ try:
+ kversion = [int(x) for x in platform.release().split("-")[0].split(".")]
+ kvok = kversion[0] > 5 or (kversion[0] == 5 and kversion[1] >= 8)
+ except ValueError:
+ kvok = False
+ if (
+ not kvok
+ or sys.version_info[0] < 3
+ or (sys.version_info[0] == 3 and sys.version_info[1] < 9)
+ ):
+ # get list of namespace file descriptors before we unshare
+ self.p_ns_fds = []
+ self.p_ns_fnames = []
+ tmpflags = uflags
+ for i in range(0, 64):
+ v = 1 << i
+ if (tmpflags & v) == 0:
+ continue
+ tmpflags &= ~v
+ if v in linux.namespace_files:
+ path = os.path.join("/proc/self", linux.namespace_files[v])
+ if os.path.exists(path):
+ self.p_ns_fds.append(os.open(path, 0))
+ self.p_ns_fnames.append(f"{path} -> {os.readlink(path)}")
+ self.logger.debug(
+ "%s: saving old namespace fd %s (%s)",
+ self,
+ self.p_ns_fnames[-1],
+ self.p_ns_fds[-1],
+ )
+ if not tmpflags:
+ break
+ else:
+ self.p_ns_fds = None
+ self.p_ns_fnames = None
+ self.ppid_fd = linux.pidfd_open(self.ppid)
+
+ self.logger.debug(
+ "%s: unshare to new namespaces %s",
+ self,
+ linux.clone_flag_string(uflags),
+ )
+
+ linux.unshare(uflags)
+
+ if not pid:
+ p = None
+ self.pid = None
+ self.nsenter_fork = False
+ else:
+ # Need to fork to create the PID namespace, but we need to continue
+ # running from the parent so that things like pytest work. We'll execute
+ # a mutini process to manage the child init 1 duties.
+ #
+ # We (the parent pid) can no longer create threads, due to that being
+ # restricted by the kernel. See EINVAL in clone(2).
+ #
+ p = commander.popen(
+ [mutini_path, "-v"],
+ stdin=subprocess.PIPE,
+ stdout=stdout,
+ stderr=stderr,
+ text=True,
+ # new session/pgid so signals don't propagate
+ start_new_session=True,
+ shell=False,
+ )
+ self.pid = p.pid
+ self.nsenter_fork = False
+ else:
+ # 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 munet
+ # 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)
+
+ # Use the parent unet process if we have one this will cause us to inherit
+ # the namespaces correctly even in the non-inline case.
+ parent = self.unet if self.unet else commander
+
+ p = parent.popen(
+ cmd,
+ stdin=subprocess.PIPE,
+ stdout=stdout,
+ stderr=stderr,
+ text=True,
+ start_new_session=not unet,
+ shell=False,
+ )
+
+ # The pid number returned is in the global pid namespace. For unshare_inline
+ # this can be unfortunate b/c our /proc has been remounted in our new pid
+ # namespace and won't contain global pid namespace pids. To solve for this
+ # we get all the pid values for the process below.
+
+ # See (A) above for when we need the child pid.
+ self.logger.debug("%s: namespace process: %s", self, proc_str(p))
+ self.pid = p.pid
+ if unet and unet.nsenter_fork:
+ assert not unet.unshare_inline
+ # Need child pid of p.pid
+ pgrep = unet.rootcmd.get_exec_path("pgrep")
+ # a sing fork was done
+ child_pid = unet.rootcmd.cmd_raises([pgrep, "-o", "-P", str(p.pid)])
+ self.pid = int(child_pid.strip())
+ self.logger.debug("%s: child of namespace process: %s", self, pid)
+
+ self.p = p
+
+ # Let's always have a valid value.
+ if self.pid is None:
+ self.pid = our_pid
+
+ #
+ # Let's find all our pids in the nested PID namespaces
+ #
+ if unet:
+ proc_path = unet.proc_path
+ else:
+ proc_path = self.proc_path if hasattr(self, "proc_path") else "/proc"
+ proc_path = f"{proc_path}/{self.pid}"
+
+ pid_status = open(f"{proc_path}/status", "r", encoding="ascii").read()
+ m = re.search(r"\nNSpid:((?:\t[0-9]+)+)\n", pid_status)
+ self.pids = [int(x) for x in m.group(1).strip().split("\t")]
+ assert self.pids[0] == self.pid
+
+ self.logger.debug("%s: namespace scoped pids: %s", self, self.pids)
+
+ # -----------------------------------------------
+ # Now let's wait until unshare completes it's job
+ # -----------------------------------------------
+ timeout = Timeout(30)
+ if self.pid is not None and self.pid != our_pid:
+ while (not p or not p.poll()) and not timeout.is_expired():
+ # check new namespace values against old (nsdict), unshare
+ # can actually take a bit to complete.
+ for fname in tuple(nslist):
+ # self.pid will be the global pid b/c we didn't unshare_inline
+ nspath = f"{proc_path}/ns/{fname}"
+ try:
+ nsf = os.readlink(nspath)
+ except OSError as error:
+ self.logger.debug(
+ "unswitched: error (ok) checking %s: %s", nspath, error
+ )
+ continue
+ if nsdict[fname] != nsf:
+ self.logger.debug(
+ "switched: original %s current %s", nsdict[fname], nsf
+ )
+ nslist.remove(fname)
+ elif unshare_inline:
+ logging.warning(
+ "unshare_inline not unshared %s == %s", nsdict[fname], nsf
+ )
+ else:
+ self.logger.debug(
+ "unswitched: current %s elapsed: %s", nsf, timeout.elapsed()
+ )
+ if not nslist:
+ self.logger.debug(
+ "all done waiting for unshare after %s", timeout.elapsed()
+ )
+ break
+
+ elapsed = int(timeout.elapsed())
+ if elapsed <= 3:
+ time_mod.sleep(0.1)
+ else:
+ self.logger.info(
+ "%s: unshare taking more than %ss: %s", self, elapsed, nslist
+ )
+ time_mod.sleep(1)
+
+ if p is not None and p.poll():
+ self.logger.error("%s: namespace process failed: %s", self, comm_error(p))
+ assert p.poll() is None, "unshare failed"
+
+ #
+ # Setup the pre-command to enter the target namespace from the running munet
+ # process using self.pid
+ #
+
+ if pid:
+ nsenter_fork = True
+ elif unet and unet.nsenter_fork:
+ # if unet created a pid namespace we need to enter it since we aren't
+ # entering a child pid namespace we created for the node. Otherwise
+ # we have a /proc remounted under unet, but our process is running in
+ # the root pid namepsace
+ nselm = "pid_for_children"
+ nsflags.append(f"--pid={pp / nselm}")
+ nsenter_fork = True
+ else:
+ # We dont need a fork.
+ nsflags.append("-F")
+ nsenter_fork = False
+
+ # Save nsenter values if running from root namespace
+ # we need this for the unshare_inline case when run externally (e.g., from
+ # within tmux server).
+ root_nsflags = [x.replace("%P%", str(self.pid)) for x in nsflags]
+ self.__root_base_pre_cmd = ["/usr/bin/nsenter", *root_nsflags]
+ self.__root_pre_cmd = list(self.__root_base_pre_cmd)
+
+ if unshare_inline:
+ assert unet is None
+ # We have nothing to do here since our process is now in the correct
+ # namespaces and children will inherit from us, even the PID namespace will
+ # be corrent b/c commands are run by first forking.
+ self.nsenter_fork = False
+ self.nsflags = []
+ self.__base_pre_cmd = []
+ else:
+ # We will use nsenter
+ self.nsenter_fork = nsenter_fork
+ self.nsflags = nsflags
+ self.__base_pre_cmd = list(self.__root_base_pre_cmd)
+
+ self.__pre_cmd = list(self.__base_pre_cmd)
+
+ # Always mark new mount namespaces as recursive private
+ if mount:
+ # if self.p is None and not pid:
+ self.cmd_raises_nsonly("mount --make-rprivate /")
+
+ # We need to remount the procfs for the new PID namespace, since we aren't using
+ # unshare(1) which does that for us.
+ if pid and unshare_inline:
+ assert mount
+ self.cmd_raises_nsonly("mount -t proc proc /proc")
+
+ # We do not want cmd_status in child classes (e.g., container) for
+ # the remaining setup calls in this __init__ function.
+
+ if net:
+ # Remount /sys to pickup any changes in the network, but keep root
+ # /sys/fs/cgroup. This pattern could be made generic and supported for any
+ # overlapping mounts
+ if mount:
+ tmpmnt = f"/tmp/cgm-{self.pid}"
+ self.cmd_status_nsonly(
+ f"mkdir {tmpmnt} && mount --rbind /sys/fs/cgroup {tmpmnt}"
+ )
+ rc = o = e = None
+ for i in range(0, 10):
+ rc, o, e = self.cmd_status_nsonly(
+ "mount -t sysfs sysfs /sys", warn=False
+ )
+ if not rc:
+ break
+ self.logger.debug(
+ "got error mounting new sysfs will retry: %s",
+ cmd_error(rc, o, e),
+ )
+ time_mod.sleep(1)
+ else:
+ raise Exception(cmd_error(rc, o, e))
+
+ self.cmd_status_nsonly(
+ f"mount --move {tmpmnt} /sys/fs/cgroup && rmdir {tmpmnt}"
+ )
+
+ # Original micronet code
+ # self.cmd_raises_nsonly("mount -t sysfs sysfs /sys")
+ # self.cmd_raises_nsonly(
+ # "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:
+ self.cmd_status_nsonly("hostname " + self.name)
+ nroot = subprocess.check_output("hostname")
+ if unshare_inline or (unet and unet.unshare_inline):
+ assert (
+ root_hostname != nroot
+ ), f'hostname unchanged from "{nroot}" wanted "{self.name}"'
+ else:
+ # Assert that we didn't just change the host hostname
+ assert (
+ root_hostname == nroot
+ ), f'root hostname "{root_hostname}" changed to "{nroot}"!'
+
+ if private_mounts:
+ if isinstance(private_mounts, str):
+ 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])
+
+ # this will fail if running inside the namespace with PID
+ if pid:
+ o = self.cmd_nostatus_nsonly("ls -l /proc/1/ns")
+ else:
+ o = self.cmd_nostatus_nsonly("ls -l /proc/self/ns")
+
+ self.logger.debug("namespaces:\n %s", o)
+
+ # will cache the path, which is important in delete to avoid running a shell
+ # which can hang during cleanup
+ self.ip_path = get_exec_path_host("ip")
+ if net:
+ self.cmd_status_nsonly([self.ip_path, "link", "set", "lo", "up"])
+
+ self.logger.info("%s: created", self)
+
+ def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs):
+ """Get the pre-user-command values.
+
+ The values returned here should be what is required to cause the user's command
+ to execute in the correct context (e.g., namespace, container, sshremote).
+ """
+ del kwargs
+ del ns_only
+ del use_pty
+ pre_cmd = self.__root_pre_cmd if root_level else self.__pre_cmd
+ return shlex.join(pre_cmd) if use_str else list(pre_cmd)
+
+ def tmpfs_mount(self, inner):
+ self.logger.debug("Mounting tmpfs on %s", inner)
+ self.cmd_raises("mkdir -p " + inner)
+ self.cmd_raises("mount -n -t tmpfs tmpfs " + inner)
+
+ def bind_mount(self, outer, inner):
+ self.logger.debug("Bind mounting %s on %s", outer, inner)
+ if commander.test("-f", outer):
+ self.cmd_raises(f"mkdir -p {os.path.dirname(inner)} && touch {inner}")
+ else:
+ if not commander.test("-e", outer):
+ commander.cmd_raises_nsonly(f"mkdir -p {outer}")
+ self.cmd_raises(f"mkdir -p {inner}")
+ self.cmd_raises("mount --rbind {} {} ".format(outer, inner))
+
+ def add_netns(self, ns):
+ self.logger.debug("Adding network namespace %s", ns)
+
+ 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_nsonly([self.ip_path, "netns", "add", ns])
+
+ def delete_netns(self, ns):
+ self.logger.debug("Deleting network namespace %s", ns)
+ self.cmd_raises_nsonly([self.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 = [self.ip_path, "link", "set", intf, "netns", ns]
+ if up:
+ cmd.append("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, considering an interface's possible namespace."""
+ if intf in self.ifnetns:
+ if isinstance(cmd, list):
+ assert cmd[0].endswith("ip")
+ cmd[1:1] = ["-n", self.ifnetns[intf]]
+ else:
+ assert cmd.startswith("ip ")
+ cmd = "ip -n " + self.ifnetns[intf] + cmd[2:]
+ self.cmd_raises_nsonly(cmd)
+
+ def intf_tc_cmd(self, intf, cmd):
+ """Run a tc command, considering an interface's possible namespace."""
+ if intf in self.ifnetns:
+ if isinstance(cmd, list):
+ assert cmd[0].endswith("tc")
+ cmd[1:1] = ["-n", self.ifnetns[intf]]
+ else:
+ assert cmd.startswith("tc ")
+ cmd = "tc -n " + self.ifnetns[intf] + cmd[2:]
+ self.cmd_raises_nsonly(cmd)
+
+ def set_ns_cwd(self, cwd: Union[str, Path]):
+ """Common code for changing pre_cmd and pre_nscmd."""
+ self.logger.debug("%s: new CWD %s", self, cwd)
+ self.__root_pre_cmd = self.__root_base_pre_cmd + ["--wd=" + str(cwd)]
+ if self.__pre_cmd:
+ self.__pre_cmd = self.__base_pre_cmd + ["--wd=" + str(cwd)]
+ elif self.unshare_inline:
+ os.chdir(cwd)
+
+ async def _async_delete(self):
+ if type(self) == LinuxNamespace: # pylint: disable=C0123
+ self.logger.info("%s: deleting", self)
+ else:
+ self.logger.debug("%s: LinuxNamespace sub-class deleting", self)
+
+ # Signal pid namespace proc to exit
+ if (
+ (self.p is None or self.p.pid != self.pid)
+ and self.pid
+ and self.pid != our_pid
+ ):
+ self.logger.debug(
+ "cleanup pid on separate pid %s from proc pid %s",
+ self.pid,
+ self.p.pid if self.p else None,
+ )
+ await self.cleanup_pid(self.pid)
+
+ if self.p is not None:
+ self.logger.debug("cleanup proc pid %s", self.p.pid)
+ await self.async_cleanup_proc(self.p)
+
+ # return to the previous namespace, need to do this in case anothe munet
+ # is being created, especially when it plans to inherit the parent's (host)
+ # namespace.
+ if self.uflags:
+ logging.info("restoring from inline unshare: cwd: %s", os.getcwd())
+ # This only works in linux>=5.8
+ if self.p_ns_fds is None:
+ self.logger.debug(
+ "%s: restoring namespaces %s",
+ self,
+ linux.clone_flag_string(self.uflags),
+ )
+ # fd = linux.pidfd_open(self.ppid)
+ fd = self.ppid_fd
+ retry = 3
+ for i in range(0, retry):
+ try:
+ linux.setns(fd, self.uflags)
+ except OSError as error:
+ self.logger.warning(
+ "%s: could not reset to old namespace fd %s: %s",
+ self,
+ fd,
+ error,
+ )
+ if i == retry - 1:
+ raise
+ time_mod.sleep(1)
+ os.close(fd)
+ else:
+ while self.p_ns_fds:
+ fd = self.p_ns_fds.pop()
+ fname = self.p_ns_fnames.pop()
+ self.logger.debug(
+ "%s: restoring namespace from fd %s (%s)", self, fname, fd
+ )
+ retry = 3
+ for i in range(0, retry):
+ try:
+ linux.setns(fd, 0)
+ break
+ except OSError as error:
+ self.logger.warning(
+ "%s: could not reset to old namespace fd %s (%s): %s",
+ self,
+ fname,
+ fd,
+ error,
+ )
+ if i == retry - 1:
+ raise
+ time_mod.sleep(1)
+ os.close(fd)
+ self.p_ns_fds = None
+ self.p_ns_fnames = None
+ logging.info("restored from unshare: cwd: %s", os.getcwd())
+
+ self.__root_base_pre_cmd = ["/bin/false"]
+ self.__base_pre_cmd = ["/bin/false"]
+ self.__root_pre_cmd = ["/bin/false"]
+ self.__pre_cmd = ["/bin/false"]
+
+ await super()._async_delete()
+
+
+class SharedNamespace(Commander):
+ """Share another namespace.
+
+ An object that executes commands in an existing pid's linux namespace
+ """
+
+ def __init__(self, name, pid=None, nsflags=None, **kwargs):
+ """Share a linux namespace.
+
+ Args:
+ name: Internal name for the namespace.
+ pid: PID of the process to share with.
+ nsflags: nsenter flags to pass to inherit namespaces from
+ """
+ super().__init__(name, **kwargs)
+
+ self.logger.debug("%s: Creating", self)
+
+ self.cwd = os.path.abspath(os.getcwd())
+ self.pid = pid if pid is not None else our_pid
+
+ nsflags = (x.replace("%P%", str(self.pid)) for x in nsflags) if nsflags else []
+ self.__base_pre_cmd = ["/usr/bin/nsenter", *nsflags] if nsflags else []
+ self.__pre_cmd = self.__base_pre_cmd
+ self.ip_path = self.get_exec_path("ip")
+
+ def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs):
+ """Get the pre-user-command values.
+
+ The values returned here should be what is required to cause the user's command
+ to execute in the correct context (e.g., namespace, container, sshremote).
+ """
+ del kwargs
+ del ns_only
+ del use_pty
+ assert not root_level
+ return shlex.join(self.__pre_cmd) if use_str else list(self.__pre_cmd)
+
+ def set_ns_cwd(self, cwd: Union[str, Path]):
+ """Common code for changing pre_cmd and pre_nscmd."""
+ self.logger.debug("%s: new CWD %s", self, cwd)
+ self.__pre_cmd = self.__base_pre_cmd + ["--wd=" + str(cwd)]
+
+
+class Bridge(SharedNamespace, InterfaceMixin):
+ """A linux bridge."""
+
+ next_ord = 1
+
+ @classmethod
+ def _get_next_id(cls):
+ # Do not use `cls` here b/c that makes the variable class specific
+ n = Bridge.next_ord
+ Bridge.next_ord = n + 1
+ return n
+
+ def __init__(self, name=None, mtu=None, unet=None, **kwargs):
+ """Create a linux Bridge."""
+ self.id = self._get_next_id()
+ if not name:
+ name = "br{}".format(self.id)
+
+ unet_pid = our_pid if unet.pid is None else unet.pid
+
+ super().__init__(name, pid=unet_pid, nsflags=unet.nsflags, unet=unet, **kwargs)
+
+ self.set_intf_basename(self.name + "-e")
+
+ self.mtu = mtu
+
+ self.logger.debug("Bridge: Creating")
+
+ assert len(self.name) <= 16 # Make sure fits in IFNAMSIZE
+ self.cmd_raises(f"ip link delete {name} || true")
+ self.cmd_raises(f"ip link add {name} type bridge")
+ if self.mtu:
+ self.cmd_raises(f"ip link set {name} mtu {self.mtu}")
+ self.cmd_raises(f"ip link set {name} up")
+
+ self.logger.debug("%s: Created, Running", self)
+
+ def get_ifname(self, netname):
+ return self.net_intfs[netname] if netname in self.net_intfs else None
+
+ async def _async_delete(self):
+ """Stop the bridge (i.e., delete the linux resources)."""
+ if type(self) == Bridge: # pylint: disable=C0123
+ self.logger.info("%s: deleting", self)
+ else:
+ self.logger.debug("%s: Bridge sub-class deleting", self)
+
+ rc, o, e = await self.async_cmd_status(
+ [self.ip_path, "link", "show", self.name],
+ stdin=subprocess.DEVNULL,
+ start_new_session=True,
+ warn=False,
+ )
+ if not rc:
+ rc, o, e = await self.async_cmd_status(
+ [self.ip_path, "link", "delete", self.name],
+ stdin=subprocess.DEVNULL,
+ start_new_session=True,
+ warn=False,
+ )
+ if rc:
+ self.logger.error(
+ "%s: error deleting bridge %s: %s",
+ self,
+ self.name,
+ cmd_error(rc, o, e),
+ )
+ await super()._async_delete()
+
+
+class BaseMunet(LinuxNamespace):
+ """Munet."""
+
+ def __init__(
+ self,
+ name="munet",
+ isolated=True,
+ pid=True,
+ rundir=None,
+ pytestconfig=None,
+ **kwargs,
+ ):
+ """Create a Munet."""
+ # logging.warning("BaseMunet: %s", name)
+
+ self.hosts = {}
+ self.switches = {}
+ self.links = {}
+ self.macs = {}
+ self.rmacs = {}
+ self.isolated = isolated
+
+ self.cli_server = None
+ self.cli_sockpath = None
+ self.cli_histfile = None
+ self.cli_in_window_cmds = {}
+ self.cli_run_cmds = {}
+
+ #
+ # We need a directory for various files
+ #
+ if not rundir:
+ rundir = "/tmp/munet"
+ self.rundir = Path(rundir)
+
+ #
+ # Always having a global /proc is required to keep things from exploding
+ # complexity with nested new pid namespaces..
+ #
+ if pid:
+ self.proc_path = Path(tempfile.mkdtemp(suffix="-proc", prefix="mu-"))
+ logging.debug("%s: mounting /proc on proc_path %s", name, self.proc_path)
+ linux.mount("proc", str(self.proc_path), "proc")
+ else:
+ self.proc_path = Path("/proc")
+
+ #
+ # Now create a root level commander that works regardless of whether we inline
+ # unshare or not. Save it in the global variable as well
+ #
+
+ if not self.isolated:
+ self.rootcmd = commander
+ elif not pid:
+ nsflags = (
+ f"--mount={self.proc_path / '1/ns/mnt'}",
+ f"--net={self.proc_path / '1/ns/net'}",
+ f"--uts={self.proc_path / '1/ns/uts'}",
+ # f"--ipc={self.proc_path / '1/ns/ipc'}",
+ # f"--time={self.proc_path / '1/ns/time'}",
+ # f"--cgroup={self.proc_path / '1/ns/cgroup'}",
+ )
+ self.rootcmd = SharedNamespace("root", pid=1, nsflags=nsflags)
+ else:
+ # XXX user
+ nsflags = (
+ # XXX Backing up PID namespace just doesn't work.
+ # f"--pid={self.proc_path / '1/ns/pid_for_children'}",
+ f"--mount={self.proc_path / '1/ns/mnt'}",
+ f"--net={self.proc_path / '1/ns/net'}",
+ f"--uts={self.proc_path / '1/ns/uts'}",
+ # f"--ipc={self.proc_path / '1/ns/ipc'}",
+ # f"--time={self.proc_path / '1/ns/time'}",
+ # f"--cgroup={self.proc_path / '1/ns/cgroup'}",
+ )
+ self.rootcmd = SharedNamespace("root", pid=1, nsflags=nsflags)
+ global roothost # pylint: disable=global-statement
+
+ roothost = self.rootcmd
+
+ self.cfgopt = munet_config.ConfigOptionsProxy(pytestconfig)
+
+ super().__init__(
+ name, mount=True, net=isolated, uts=isolated, pid=pid, unet=None, **kwargs
+ )
+
+ # This allows us to cleanup any leftover running munet's
+ if "MUNET_PID" in os.environ:
+ if os.environ["MUNET_PID"] != str(our_pid):
+ logging.error(
+ "Found env MUNET_PID != our pid %s, instead its %s, changing",
+ our_pid,
+ os.environ["MUNET_PID"],
+ )
+ os.environ["MUNET_PID"] = str(our_pid)
+
+ # this is for testing purposes do not use
+ if not BaseMunet.g_unet:
+ BaseMunet.g_unet = self
+
+ self.logger.debug("%s: Creating", self)
+
+ 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 munet."""
+ self.logger.debug("%s: add_host %s(%s)", self, cls.__name__, name)
+
+ self.hosts[name] = cls(name, unet=self, **kwargs)
+
+ # Create a new mounted FS for tracking nested network namespaces creatd by the
+ # user with `ip netns add`
+
+ # XXX why is this failing with podman???
+ # self.hosts[name].tmpfs_mount("/run/netns")
+
+ return self.hosts[name]
+
+ def add_link(self, node1, node2, if1, if2, mtu=None, **intf_constraints):
+ """Add a link between switch and node or 2 nodes.
+
+ If constraints are given they are applied to each endpoint. See
+ `InterfaceMixin::set_intf_constraints()` for more info.
+ """
+ isp2p = False
+
+ try:
+ name1 = node1.name
+ except AttributeError:
+ if node1 in self.switches:
+ node1 = self.switches[node1]
+ else:
+ node1 = self.hosts[node1]
+ name1 = node1.name
+
+ try:
+ name2 = node2.name
+ except AttributeError:
+ if node2 in self.switches:
+ node2 = self.switches[node2]
+ else:
+ node2 = self.hosts[node2]
+ name2 = node2.name
+
+ 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)
+
+ # Done at root level
+ nsif1 = lhost.get_ns_ifname(if1)
+ nsif2 = rhost.get_ns_ifname(if2)
+
+ # Use pids[-1] to get the unet scoped pid for hosts
+ self.cmd_raises_nsonly(
+ f"ip link add {lifname} type veth peer name {nsif2}"
+ f" netns {rhost.pids[-1]}"
+ )
+ self.cmd_raises_nsonly(f"ip link set {lifname} netns {lhost.pids[-1]}")
+
+ lhost.cmd_raises_nsonly("ip link set {} name {}".format(lifname, nsif1))
+ if mtu:
+ lhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif1, mtu))
+ lhost.cmd_raises_nsonly("ip link set {} up".format(nsif1))
+ lhost.register_interface(if1)
+
+ if mtu:
+ rhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif2, mtu))
+ rhost.cmd_raises_nsonly("ip link set {} up".format(nsif2))
+ rhost.register_interface(if2)
+ else:
+ switch = self.switches[name1]
+ rhost = self.hosts[name2]
+
+ nsif1 = switch.get_ns_ifname(if1)
+ nsif2 = rhost.get_ns_ifname(if2)
+
+ if mtu is None:
+ mtu = switch.mtu
+
+ if len(nsif1) > 16:
+ self.logger.error('"%s" len %s > 16', nsif1, len(nsif1))
+ elif len(nsif2) > 16:
+ self.logger.error('"%s" len %s > 16', nsif2, len(nsif2))
+ assert len(nsif1) <= 16 and len(nsif2) <= 16 # Make sure fits in IFNAMSIZE
+
+ self.logger.debug("%s: Creating veth pair for link %s", self, lname)
+
+ # Use pids[-1] to get the unet scoped pid for hosts
+ # switch is already in our namespace so nothing to convert.
+ self.cmd_raises_nsonly(
+ f"ip link add {nsif1} type veth peer name {nsif2}"
+ f" netns {rhost.pids[-1]}"
+ )
+
+ if mtu:
+ # if switch.mtu:
+ # # the switch interface should match the switch config
+ # switch.cmd_raises_nsonly(
+ # "ip link set {} mtu {}".format(if1, switch.mtu)
+ # )
+ switch.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif1, mtu))
+ rhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif2, mtu))
+
+ switch.register_interface(if1)
+ rhost.register_interface(if2)
+ rhost.register_network(switch.name, if2)
+
+ switch.cmd_raises_nsonly(f"ip link set {nsif1} master {switch.name}")
+
+ switch.cmd_raises_nsonly(f"ip link set {nsif1} up")
+ rhost.cmd_raises_nsonly(f"ip link set {nsif2} up")
+
+ # Cache the MAC values, and reverse mapping
+ self.get_mac(name1, nsif1)
+ self.get_mac(name2, nsif2)
+
+ # Setup interface constraints if provided
+ if intf_constraints:
+ node1.set_intf_constraints(if1, **intf_constraints)
+ node2.set_intf_constraints(if2, **intf_constraints)
+
+ def add_switch(self, name, cls=Bridge, **kwargs):
+ """Add a switch to munet."""
+ self.logger.debug("%s: add_switch %s(%s)", self, cls.__name__, name)
+ self.switches[name] = cls(name, unet=self, **kwargs)
+ return self.switches[name]
+
+ def get_mac(self, name, ifname):
+ if name in self.hosts:
+ dev = self.hosts[name]
+ else:
+ dev = self.switches[name]
+
+ nsifname = self.get_ns_ifname(ifname)
+
+ if (name, ifname) not in self.macs:
+ _, output, _ = dev.cmd_status_nsonly("ip -o link show " + nsifname)
+ 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)]
+
+ async def _delete_link(self, lname):
+ rname, rif = self.links[lname][2:4]
+ host = self.hosts[rname]
+ nsrif = host.get_ns_ifname(rif)
+
+ self.logger.debug("%s: Deleting veth pair for link %s", self, lname)
+ rc, o, e = await host.async_cmd_status_nsonly(
+ [self.ip_path, "link", "delete", nsrif],
+ stdin=subprocess.DEVNULL,
+ start_new_session=True,
+ warn=False,
+ )
+ if rc:
+ self.logger.error("Err del veth pair %s: %s", lname, cmd_error(rc, o, e))
+
+ async def _delete_links(self):
+ # for x in self.links:
+ # await self._delete_link(x)
+ return await asyncio.gather(*[self._delete_link(x) for x in self.links])
+
+ async def _async_delete(self):
+ """Delete the munet topology."""
+ # logger = self.logger if False else logging
+ logger = self.logger
+ if type(self) == BaseMunet: # pylint: disable=C0123
+ logger.info("%s: deleting.", self)
+ else:
+ logger.debug("%s: BaseMunet sub-class deleting.", self)
+
+ logger.debug("Deleting links")
+ try:
+ await self._delete_links()
+ except Exception as error:
+ logger.error("%s: error deleting links: %s", self, error, exc_info=True)
+
+ logger.debug("Deleting hosts and bridges")
+ try:
+ # Delete hosts and switches, wait for them all to complete
+ # even if there is an exception.
+ htask = [x.async_delete() for x in self.hosts.values()]
+ stask = [x.async_delete() for x in self.switches.values()]
+ await asyncio.gather(*htask, *stask, return_exceptions=True)
+ except Exception as error:
+ logger.error(
+ "%s: error deleting hosts and switches: %s", self, error, exc_info=True
+ )
+
+ self.links = {}
+ self.hosts = {}
+ self.switches = {}
+
+ try:
+ if self.cli_server:
+ self.cli_server.cancel()
+ self.cli_server = None
+ if self.cli_sockpath:
+ await self.async_cmd_status(
+ "rm -rf " + os.path.dirname(self.cli_sockpath)
+ )
+ self.cli_sockpath = None
+ except Exception as error:
+ logger.error(
+ "%s: error cli server or sockpaths: %s", self, error, exc_info=True
+ )
+
+ try:
+ if self.cli_histfile:
+ readline.write_history_file(self.cli_histfile)
+ self.cli_histfile = None
+ except Exception as error:
+ logger.error(
+ "%s: error saving history file: %s", self, error, exc_info=True
+ )
+
+ # XXX for some reason setns during the delete is changing our dir to /.
+ cwd = os.getcwd()
+
+ try:
+ await super()._async_delete()
+ except Exception as error:
+ logger.error(
+ "%s: error deleting parent classes: %s", self, error, exc_info=True
+ )
+ os.chdir(cwd)
+
+ try:
+ if self.proc_path and str(self.proc_path) != "/proc":
+ logger.debug("%s: umount, remove proc_path %s", self, self.proc_path)
+ linux.umount(str(self.proc_path), 0)
+ os.rmdir(self.proc_path)
+ except Exception as error:
+ logger.warning(
+ "%s: error umount and removing proc_path %s: %s",
+ self,
+ self.proc_path,
+ error,
+ exc_info=True,
+ )
+ try:
+ linux.umount(str(self.proc_path), linux.MNT_DETACH)
+ except Exception as error2:
+ logger.error(
+ "%s: error umount with detach proc_path %s: %s",
+ self,
+ self.proc_path,
+ error2,
+ exc_info=True,
+ )
+
+ if BaseMunet.g_unet == self:
+ BaseMunet.g_unet = None
+
+
+BaseMunet.g_unet = None
+
+if True: # pylint: disable=using-constant-test
+
+ class ShellWrapper:
+ """A Read-Execute-Print-Loop (REPL) interface.
+
+ A newline or prompt changing command should be sent to the
+ spawned child prior to creation as the `prompt` will be `expect`ed
+ """
+
+ def __init__(
+ self,
+ spawn,
+ prompt,
+ continuation_prompt=None,
+ extra_init_cmd=None,
+ will_echo=False,
+ escape_ansi=False,
+ ):
+ self.echo = will_echo
+ self.escape = (
+ re.compile(r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]") if escape_ansi else None
+ )
+
+ logging.debug(
+ 'ShellWraper: XXX prompt "%s" will_echo %s child.echo %s',
+ prompt,
+ will_echo,
+ spawn.echo,
+ )
+
+ self.child = spawn
+ if self.child.echo:
+ logging.info("Setting child to echo")
+ self.child.setecho(False)
+ self.child.waitnoecho()
+ assert not self.child.echo
+
+ self.prompt = prompt
+ self.cont_prompt = continuation_prompt
+
+ # Use expect_exact if we can as it should be faster
+ self.expects = [prompt]
+ if re.escape(prompt) == prompt and hasattr(self.child, "expect_exact"):
+ self._expectf = self.child.expect_exact
+ else:
+ self._expectf = self.child.expect
+ if continuation_prompt:
+ self.expects.append(continuation_prompt)
+ if re.escape(continuation_prompt) != continuation_prompt:
+ self._expectf = self.child.expect
+
+ if extra_init_cmd:
+ self.expect_prompt()
+ self.child.sendline(extra_init_cmd)
+ self.expect_prompt()
+
+ def expect_prompt(self, timeout=-1):
+ return self._expectf(self.expects, timeout=timeout)
+
+ def run_command(self, command, timeout=-1):
+ """Pexpect REPLWrapper compatible run_command.
+
+ This will split `command` into lines and feed each one to the shell.
+
+ Args:
+ command: string of commands separated by newlines, a trailing
+ newline will cause and empty line to be sent.
+ timeout: pexpect timeout value.
+ """
+ lines = command.splitlines()
+ if command[-1] == "\n":
+ lines.append("")
+ output = ""
+ index = 0
+ for line in lines:
+ self.child.sendline(line)
+ index = self.expect_prompt(timeout=timeout)
+ output += self.child.before
+
+ if index:
+ if hasattr(self.child, "kill"):
+ self.child.kill(signal.SIGINT)
+ else:
+ self.child.send("\x03")
+ self.expect_prompt(timeout=30 if self.child.timeout is None else -1)
+ raise ValueError("Continuation prompt found at end of commands")
+
+ if self.escape:
+ output = self.escape.sub("", output)
+
+ return output
+
+ def cmd_nostatus(self, cmd, timeout=-1):
+ r"""Execute a shell command.
+
+ Returns:
+ (strip/cleaned \r) output
+ """
+ output = self.run_command(cmd, timeout)
+ output = output.replace("\r\n", "\n")
+ if self.echo:
+ # remove the command
+ idx = output.find(cmd)
+ if idx == -1:
+ logging.warning(
+ "Didn't find command ('%s') in expected output ('%s')",
+ cmd,
+ output,
+ )
+ else:
+ # Remove up to and including the command from the output stream
+ output = output[idx + len(cmd) :]
+
+ return output.replace("\r", "").strip()
+
+ def cmd_status(self, cmd, timeout=-1):
+ r"""Execute a shell command.
+
+ Returns:
+ status and (strip/cleaned \r) output
+ """
+ # Run the command getting the output
+ output = self.cmd_nostatus(cmd, timeout)
+
+ # Now get the status
+ scmd = "echo $?"
+ rcstr = self.run_command(scmd)
+ rcstr = rcstr.replace("\r\n", "\n")
+ if self.echo:
+ # remove the command
+ idx = rcstr.find(scmd)
+ if idx == -1:
+ if self.echo:
+ logging.warning(
+ "Didn't find status ('%s') in expected output ('%s')",
+ scmd,
+ rcstr,
+ )
+ try:
+ rc = int(rcstr)
+ except Exception:
+ rc = 255
+ else:
+ rcstr = rcstr[idx + len(scmd) :].strip()
+ try:
+ rc = int(rcstr)
+ except ValueError as error:
+ logging.error(
+ "%s: error with expected status output: %s: %s",
+ self,
+ error,
+ rcstr,
+ exc_info=True,
+ )
+ rc = 255
+ return rc, output
+
+ def cmd_raises(self, cmd, timeout=-1):
+ r"""Execute a shell command.
+
+ Returns:
+ (strip/cleaned \r) ouptut
+
+ Raises:
+ CalledProcessError: on non-zero exit status
+ """
+ rc, output = self.cmd_status(cmd, timeout)
+ if rc:
+ raise CalledProcessError(rc, cmd, output)
+ return output
+
+
+# ---------------------------
+# Root level utility function
+# ---------------------------
+
+
+def get_exec_path(binary):
+ return commander.get_exec_path(binary)
+
+
+def get_exec_path_host(binary):
+ return commander.get_exec_path(binary)
+
+
+def get_our_script_path(script):
+ # would be nice to find this w/o using a path lookup
+ sdir = os.path.dirname(os.path.abspath(__file__))
+ spath = os.path.join(sdir, script)
+ if os.path.exists(spath):
+ return spath
+ return get_exec_path(script)
+
+
+commander = Commander("munet")
+roothost = None
diff --git a/tests/topotests/munet/cleanup.py b/tests/topotests/munet/cleanup.py
new file mode 100644
index 0000000..c641cda
--- /dev/null
+++ b/tests/topotests/munet/cleanup.py
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# September 30 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""Provides functionality to cleanup processes on posix systems."""
+import glob
+import logging
+import os
+import signal
+
+
+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):
+ ourpid = str(os.getpid())
+ for upid, pids in pids_by_upid:
+ logging.info("Sending %s to (%s) of munet pid %s", sig, ", ".join(pids), upid)
+ for pid in pids:
+ try:
+ if pid != ourpid:
+ cmdline = open(f"/proc/{pid}/cmdline", "r", encoding="ascii").read()
+ cmdline = cmdline.replace("\x00", " ")
+ logging.info("killing proc %s (%s)", pid, cmdline)
+ os.kill(int(pid), sig)
+ except Exception:
+ pass
+
+
+def _get_our_pids():
+ ourpid = str(os.getpid())
+ piddict = get_pids_with_env("MUNET_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("MUNET_PID")
+ unet_pids = {d["MUNET_PID"] for d in piddict.values()}
+ pids_by_upid = {p: set() for p in unet_pids}
+ for pid, envdict in piddict.items():
+ unet_pid = envdict["MUNET_PID"]
+ pids_by_upid[unet_pid].add(pid)
+ # Filter out any child pid sets whos munet 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
+
+ t = "current" if ours else "previous"
+ logging.info("Reaping %s munet processes", t)
+
+ # _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.
+ """
+ _cleanup_pids(True)
+
+
+def cleanup_previous():
+ """Attempt to cleanup preview runs.
+
+ Currently this only scans for old processes.
+ """
+ _cleanup_pids(False)
diff --git a/tests/topotests/munet/cli.py b/tests/topotests/munet/cli.py
new file mode 100644
index 0000000..f631073
--- /dev/null
+++ b/tests/topotests/munet/cli.py
@@ -0,0 +1,962 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# July 24 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""A module that implements a CLI."""
+import argparse
+import asyncio
+import functools
+import logging
+import multiprocessing
+import os
+import pty
+import re
+import readline
+import select
+import shlex
+import socket
+import subprocess
+import sys
+import tempfile
+import termios
+import tty
+
+
+try:
+ from . import linux
+ from .config import list_to_dict_with_key
+except ImportError:
+ # We cannot use relative imports and still run this module directly as a script, and
+ # there are some use cases where we want to run this file as a script.
+ sys.path.append(os.path.dirname(os.path.realpath(__file__)))
+ import linux
+
+ from config import list_to_dict_with_key
+
+
+ENDMARKER = b"\x00END\x00"
+
+logger = logging.getLogger(__name__)
+
+
+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 :]
+
+
+# Would be nice to convert to async, but really not needed as used
+def spawn(unet, host, cmd, iow, ns_only):
+ if sys.stdin.isatty():
+ old_tty = termios.tcgetattr(sys.stdin)
+ tty.setraw(sys.stdin.fileno())
+
+ try:
+ master_fd, slave_fd = pty.openpty()
+
+ ns = unet.hosts[host] if host and host != unet else unet
+ popenf = ns.popen_nsonly if ns_only else ns.popen
+
+ # use os.setsid() make it run in a new process group, or bash job
+ # control will not be enabled
+ p = popenf(
+ cmd,
+ # _common_prologue, later in call chain, only does this for use_pty == False
+ preexec_fn=os.setsid,
+ stdin=slave_fd,
+ stdout=slave_fd,
+ stderr=slave_fd,
+ universal_newlines=True,
+ use_pty=True,
+ # XXX this is actually implementing "run on host" for real
+ # skip_pre_cmd=ns_only,
+ )
+ iow.write("\r")
+ iow.flush()
+
+ while p.poll() is None:
+ r, _, _ = 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:
+ iow.write(o.decode("utf-8", "ignore"))
+ iow.flush()
+ finally:
+ # restore tty settings back
+ if sys.stdin.isatty():
+ termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
+
+
+def is_host_regex(restr):
+ return len(restr) > 2 and restr[0] == "/" and restr[-1] == "/"
+
+
+def get_host_regex(restr):
+ if len(restr) < 3 or restr[0] != "/" or restr[-1] != "/":
+ return None
+ return re.compile(restr[1:-1])
+
+
+def host_in(restr, names):
+ """Determine if matcher is a regex that matches one of names."""
+ if not (regexp := get_host_regex(restr)):
+ return restr in names
+ for name in names:
+ if regexp.fullmatch(name):
+ return True
+ return False
+
+
+def expand_host(restr, names):
+ """Expand name or regexp into list of hosts."""
+ hosts = []
+ regexp = get_host_regex(restr)
+ if not regexp:
+ assert restr in names
+ hosts.append(restr)
+ else:
+ for name in names:
+ if regexp.fullmatch(name):
+ hosts.append(name)
+ return sorted(hosts)
+
+
+def expand_hosts(restrs, names):
+ """Expand list of host names or regex into list of hosts."""
+ hosts = []
+ for restr in restrs:
+ hosts += expand_host(restr, names)
+ return sorted(hosts)
+
+
+def host_cmd_split(unet, line, toplevel):
+ all_hosts = set(unet.hosts)
+ csplit = line.split()
+ i = 0
+ banner = False
+ for i, e in enumerate(csplit):
+ if is_re := is_host_regex(e):
+ banner = True
+ if not host_in(e, all_hosts):
+ if not is_re:
+ break
+ else:
+ i += 1
+
+ if i == 0 and csplit and csplit[0] == "*":
+ hosts = sorted(all_hosts)
+ csplit = csplit[1:]
+ banner = True
+ elif i == 0 and csplit and csplit[0] == ".":
+ hosts = [unet]
+ csplit = csplit[1:]
+ else:
+ hosts = expand_hosts(csplit[:i], all_hosts)
+ csplit = csplit[i:]
+
+ if not hosts and not csplit[:i]:
+ if toplevel:
+ hosts = [unet]
+ else:
+ hosts = sorted(all_hosts)
+ banner = True
+
+ if not csplit:
+ return hosts, "", "", True
+
+ i = line.index(csplit[0])
+ i += len(csplit[0])
+ return hosts, csplit[0], line[i:].strip(), banner
+
+
+def win_cmd_host_split(unet, cmd, kinds, defall):
+ if kinds:
+ all_hosts = {
+ x for x in unet.hosts if unet.hosts[x].config.get("kind", "") in kinds
+ }
+ else:
+ all_hosts = set(unet.hosts)
+
+ csplit = cmd.split()
+ i = 0
+ for i, e in enumerate(csplit):
+ if not host_in(e, all_hosts):
+ if not is_host_regex(e):
+ break
+ else:
+ i += 1
+
+ if i == 0 and csplit and csplit[0] == "*":
+ hosts = sorted(all_hosts)
+ csplit = csplit[1:]
+ elif i == 0 and csplit and csplit[0] == ".":
+ hosts = [unet]
+ csplit = csplit[1:]
+ else:
+ hosts = expand_hosts(csplit[:i], all_hosts)
+
+ if not hosts and defall and not csplit[:i]:
+ hosts = sorted(all_hosts)
+
+ # Filter hosts based on cmd
+ cmd = " ".join(csplit[i:])
+ return hosts, cmd
+
+
+def proc_readline(fd, prompt, histfile):
+ """Read a line of input from user while running in a sub-process."""
+ # How do we change the command though, that's what's displayed in ps normally
+ linux.set_process_name("Munet CLI")
+ try:
+ # For some reason sys.stdin is fileno == 16 and useless
+ sys.stdin = os.fdopen(0)
+ histfile = init_history(None, histfile)
+ line = input(prompt)
+ readline.write_history_file(histfile)
+ if line is None:
+ os.write(fd, b"\n")
+ os.write(fd, bytes(f":{str(line)}\n", encoding="utf-8"))
+ except EOFError:
+ os.write(fd, b"\n")
+ except KeyboardInterrupt:
+ os.write(fd, b"I\n")
+ except Exception as error:
+ os.write(fd, bytes(f"E{str(error)}\n", encoding="utf-8"))
+
+
+async def async_input_reader(rfd):
+ """Read a line of input from the user input sub-process pipe."""
+ rpipe = os.fdopen(rfd, mode="r")
+ reader = asyncio.StreamReader()
+
+ def protocol_factory():
+ return asyncio.StreamReaderProtocol(reader)
+
+ loop = asyncio.get_event_loop()
+ transport, _ = await loop.connect_read_pipe(protocol_factory, rpipe)
+ o = await reader.readline()
+ transport.close()
+
+ o = o.decode("utf-8").strip()
+ if not o:
+ return None
+ if o[0] == "I":
+ raise KeyboardInterrupt()
+ if o[0] == "E":
+ raise Exception(o[1:])
+ assert o[0] == ":"
+ return o[1:]
+
+
+#
+# A lot of work to add async `input` handling without creating a thread. We cannot use
+# threads when unshare_inline is used with pid namespace per kernel clone(2)
+# restriction.
+#
+async def async_input(prompt, histfile):
+ """Asynchronously read a line from the user."""
+ rfd, wfd = os.pipe()
+ p = multiprocessing.Process(target=proc_readline, args=(wfd, prompt, histfile))
+ p.start()
+ logging.debug("started async_input input process: %s", p)
+ try:
+ return await async_input_reader(rfd)
+ finally:
+ logging.debug("joining async_input input process")
+ p.join()
+
+
+def make_help_str(unet):
+ w = sorted([x if x else "" for x in unet.cli_in_window_cmds])
+ ww = unet.cli_in_window_cmds
+ u = sorted([x if x else "" for x in unet.cli_run_cmds])
+ uu = unet.cli_run_cmds
+
+ s = (
+ """
+Basic Commands:
+ cli :: open a secondary CLI window
+ help :: this help
+ hosts :: list hosts
+ quit :: quit the cli
+
+ HOST can be a host or one of the following:
+ - '*' for all hosts
+ - '.' for the parent munet
+ - a regex specified between '/' (e.g., '/rtr.*/')
+
+New Window Commands:\n"""
+ + "\n".join([f" {ww[v][0]}\t:: {ww[v][1]}" for v in w])
+ + """\nInline Commands:\n"""
+ + "\n".join([f" {uu[v][0]}\t:: {uu[v][1]}" for v in u])
+ + "\n"
+ )
+ return s
+
+
+def get_shcmd(unet, host, kinds, execfmt, ucmd):
+ if host is None:
+ h = None
+ kind = None
+ elif host is unet or host == "":
+ h = unet
+ kind = ""
+ else:
+ h = unet.hosts[host]
+ kind = h.config.get("kind", "")
+ if kinds and kind not in kinds:
+ return ""
+ if not isinstance(execfmt, str):
+ execfmt = execfmt.get(kind, {}).get("exec", "")
+ if not execfmt:
+ return ""
+
+ # Do substitutions for {} in string
+ numfmt = len(re.findall(r"{\d*}", execfmt))
+ if numfmt > 1:
+ ucmd = execfmt.format(*shlex.split(ucmd))
+ elif numfmt:
+ ucmd = execfmt.format(ucmd)
+ elif len(re.findall(r"{[a-zA-Z_][0-9a-zA-Z_\.]*}", execfmt)):
+ if execfmt.endswith('"'):
+ fstring = "f'''" + execfmt + "'''"
+ else:
+ fstring = 'f"""' + execfmt + '"""'
+ ucmd = eval( # pylint: disable=W0123
+ fstring,
+ globals(),
+ {"host": h, "unet": unet, "user_input": ucmd},
+ )
+ else:
+ # No variable or usercmd substitution at all.
+ ucmd = execfmt
+
+ # Do substitution for munet variables
+ ucmd = ucmd.replace("%CONFIGDIR%", str(unet.config_dirname))
+ if host is None or host is unet:
+ ucmd = ucmd.replace("%RUNDIR%", str(unet.rundir))
+ return ucmd.replace("%NAME%", ".")
+ ucmd = ucmd.replace("%RUNDIR%", str(os.path.join(unet.rundir, host)))
+ if h.mgmt_ip:
+ ucmd = ucmd.replace("%IPADDR%", str(h.mgmt_ip))
+ elif h.mgmt_ip6:
+ ucmd = ucmd.replace("%IPADDR%", str(h.mgmt_ip6))
+ if h.mgmt_ip6:
+ ucmd = ucmd.replace("%IP6ADDR%", str(h.mgmt_ip6))
+ return ucmd.replace("%NAME%", str(host))
+
+
+async def run_command(
+ unet,
+ outf,
+ line,
+ execfmt,
+ banner,
+ hosts,
+ toplevel,
+ kinds,
+ ns_only=False,
+ interactive=False,
+):
+ """Runs a command on a set of hosts.
+
+ Runs `execfmt`. Prior to executing the string the following transformations are
+ performed on it.
+
+ `execfmt` may also be a dictionary of dicitonaries keyed on kind with `exec` holding
+ the kind's execfmt string.
+
+ - if `{}` is present then `str.format` is called to replace `{}` with any extra
+ input values after the command and hosts are removed from the input.
+ - else if any `{digits}` are present then `str.format` is called to replace
+ `{digits}` with positional args obtained from the addittional user input
+ first passed to `shlex.split`.
+ - else f-string style interpolation is performed on the string with
+ the local variables `host` (the current node object or None),
+ `unet` (the Munet object), and `user_input` (the additional command input)
+ defined.
+
+ The output is sent to `outf`. If `ns_only` is True then the `execfmt` is
+ run using `Commander.cmd_status_nsonly` otherwise it is run with
+ `Commander.cmd_status`.
+ """
+ if kinds:
+ logging.info("Filtering hosts to kinds: %s", kinds)
+ hosts = [x for x in hosts if unet.hosts[x].config.get("kind", "") in kinds]
+ logging.info("Filtered hosts: %s", hosts)
+
+ if not hosts:
+ if not toplevel:
+ return
+ hosts = [unet]
+
+ # if unknowns := [x for x in hosts if x not in unet.hosts]:
+ # outf.write("%% Unknown host[s]: %s\n" % ", ".join(unknowns))
+ # return
+
+ # if sys.stdin.isatty() and interactive:
+ if interactive:
+ for host in hosts:
+ shcmd = get_shcmd(unet, host, kinds, execfmt, line)
+ if not shcmd:
+ continue
+ if len(hosts) > 1 or banner:
+ outf.write(f"------ Host: {host} ------\n")
+ spawn(unet, host if not toplevel else unet, shcmd, outf, ns_only)
+ if len(hosts) > 1 or banner:
+ outf.write(f"------- End: {host} ------\n")
+ outf.write("\n")
+ return
+
+ aws = []
+ for host in hosts:
+ shcmd = get_shcmd(unet, host, kinds, execfmt, line)
+ if not shcmd:
+ continue
+ if toplevel:
+ ns = unet
+ else:
+ ns = unet.hosts[host] if host and host != unet else unet
+ if ns_only:
+ cmdf = ns.async_cmd_status_nsonly
+ else:
+ cmdf = ns.async_cmd_status
+ aws.append(cmdf(shcmd, warn=False, stderr=subprocess.STDOUT))
+
+ results = await asyncio.gather(*aws, return_exceptions=True)
+ for host, result in zip(hosts, results):
+ if isinstance(result, Exception):
+ o = str(result) + "\n"
+ rc = -1
+ else:
+ rc, o, _ = result
+ if len(hosts) > 1 or banner:
+ outf.write(f"------ Host: {host} ------\n")
+ if rc:
+ outf.write(f"*** non-zero exit status: {rc}\n")
+ outf.write(o)
+ if len(hosts) > 1 or banner:
+ outf.write(f"------- End: {host} ------\n")
+
+
+cli_builtins = ["cli", "help", "hosts", "quit"]
+
+
+class Completer:
+ """A completer class for the CLI."""
+
+ def __init__(self, unet):
+ self.unet = unet
+
+ def complete(self, text, state):
+ line = readline.get_line_buffer()
+ tokens = line.split()
+ # print(f"\nXXX: tokens: {tokens} text: '{text}' state: {state}'\n")
+
+ first_token = not tokens or (text and len(tokens) == 1)
+
+ # If we have already have a builtin command we are done
+ if tokens and tokens[0] in cli_builtins:
+ return [None]
+
+ cli_run_cmds = set(self.unet.cli_run_cmds.keys())
+ top_run_cmds = {x for x in cli_run_cmds if self.unet.cli_run_cmds[x][3]}
+ cli_run_cmds -= top_run_cmds
+ cli_win_cmds = set(self.unet.cli_in_window_cmds.keys())
+ hosts = set(self.unet.hosts.keys())
+ is_window_cmd = bool(tokens) and tokens[0] in cli_win_cmds
+ done_set = set()
+ if bool(tokens):
+ if text:
+ done_set = set(tokens[:-1])
+ else:
+ done_set = set(tokens)
+
+ # Determine the domain for completions
+ if not tokens or first_token:
+ all_cmds = (
+ set(cli_builtins) | hosts | cli_run_cmds | cli_win_cmds | top_run_cmds
+ )
+ elif is_window_cmd:
+ all_cmds = hosts
+ elif tokens and tokens[0] in top_run_cmds:
+ # nothing to complete if a top level command
+ pass
+ elif not bool(done_set & cli_run_cmds):
+ all_cmds = hosts | cli_run_cmds
+
+ if not text:
+ completes = all_cmds
+ else:
+ # print(f"\nXXX: all_cmds: {all_cmds} text: '{text}'\n")
+ completes = {x + " " for x in all_cmds if x.startswith(text)}
+
+ # print(f"\nXXX: completes: {completes} text: '{text}' state: {state}'\n")
+ # remove any completions already present
+ completes -= done_set
+ completes = sorted(completes) + [None]
+ return completes[state]
+
+
+async def doline(
+ unet, line, outf, background=False, notty=False
+): # pylint: disable=R0911
+ line = line.strip()
+ m = re.fullmatch(r"^(\S+)(?:\s+(.*))?$", line)
+ if not m:
+ return True
+
+ cmd = m.group(1)
+ nline = m.group(2) if m.group(2) else ""
+
+ if cmd in ("q", "quit"):
+ return False
+
+ if cmd == "help":
+ outf.write(make_help_str(unet))
+ return True
+ if cmd in ("h", "hosts"):
+ outf.write(f"% Hosts:\t{' '.join(sorted(unet.hosts.keys()))}\n")
+ return True
+ if cmd == "cli":
+ await remote_cli(
+ unet,
+ "secondary> ",
+ "Secondary CLI",
+ background,
+ )
+ return True
+
+ #
+ # In window commands
+ #
+
+ if cmd in unet.cli_in_window_cmds:
+ execfmt, toplevel, kinds, kwargs = unet.cli_in_window_cmds[cmd][2:]
+
+ # if toplevel:
+ # ucmd = " ".join(nline.split())
+ # else:
+ hosts, ucmd = win_cmd_host_split(unet, nline, kinds, False)
+ if not hosts:
+ if not toplevel:
+ return True
+ hosts = [unet]
+
+ if isinstance(execfmt, str):
+ found_brace = "{}" in execfmt
+ else:
+ found_brace = False
+ for d in execfmt.values():
+ if "{}" in d["exec"]:
+ found_brace = True
+ break
+ if not found_brace and ucmd and not toplevel:
+ # CLI command does not expect user command so treat as hosts of which some
+ # must be unknown
+ unknowns = [x for x in ucmd.split() if x not in unet.hosts]
+ outf.write(f"% Unknown host[s]: {' '.join(unknowns)}\n")
+ return True
+
+ try:
+ if not hosts and toplevel:
+ hosts = [unet]
+
+ for host in hosts:
+ shcmd = get_shcmd(unet, host, kinds, execfmt, ucmd)
+ if toplevel or host == unet:
+ unet.run_in_window(shcmd, **kwargs)
+ else:
+ unet.hosts[host].run_in_window(shcmd, **kwargs)
+ except Exception as error:
+ outf.write(f"% Error: {error}\n")
+ return True
+
+ #
+ # Inline commands
+ #
+
+ toplevel = unet.cli_run_cmds[cmd][3] if cmd in unet.cli_run_cmds else False
+ # if toplevel:
+ # logging.debug("top-level: cmd: '%s' nline: '%s'", cmd, nline)
+ # hosts = None
+ # banner = False
+ # else:
+
+ hosts, cmd, nline, banner = host_cmd_split(unet, line, toplevel)
+ hoststr = "munet" if hosts == [unet] else f"{hosts}"
+ logging.debug("hosts: '%s' cmd: '%s' nline: '%s'", hoststr, cmd, nline)
+
+ if cmd in unet.cli_run_cmds:
+ pass
+ elif "" in unet.cli_run_cmds:
+ nline = f"{cmd} {nline}"
+ cmd = ""
+ else:
+ outf.write(f"% Unknown command: {cmd} {nline}\n")
+ return True
+
+ execfmt, toplevel, kinds, ns_only, interactive = unet.cli_run_cmds[cmd][2:]
+ if interactive and notty:
+ outf.write("% Error: interactive command must be run from primary CLI\n")
+ return True
+
+ await run_command(
+ unet,
+ outf,
+ nline,
+ execfmt,
+ banner,
+ hosts,
+ toplevel,
+ kinds,
+ ns_only,
+ interactive,
+ )
+
+ return True
+
+
+async def cli_client(sockpath, prompt="munet> "):
+ """Implement the user-facing CLI for a remote munet reached by a socket."""
+ 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--- Munet CLI Starting ---\n\n")
+ while True:
+ 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", "ignore"))
+
+
+async def local_cli(unet, outf, prompt, histfile, background):
+ """Implement the user-side CLI for local munet."""
+ assert unet is not None
+ completer = Completer(unet)
+ readline.parse_and_bind("tab: complete")
+ readline.set_completer(completer.complete)
+
+ print("\n--- Munet CLI Starting ---\n\n")
+ while True:
+ try:
+ line = await async_input(prompt, histfile)
+ if line is None:
+ return
+
+ if not await doline(unet, line, outf, background):
+ return
+ except KeyboardInterrupt:
+ outf.write("%% Caught KeyboardInterrupt\nUse ^D or 'quit' to exit")
+
+
+def init_history(unet, histfile):
+ try:
+ if histfile is None:
+ histfile = os.path.expanduser("~/.munet-history.txt")
+ if not os.path.exists(histfile):
+ if unet:
+ unet.cmd("touch " + histfile)
+ else:
+ subprocess.run("touch " + histfile, shell=True, check=True)
+ if histfile:
+ readline.read_history_file(histfile)
+ return histfile
+ except Exception as error:
+ logging.warning("init_history failed: %s", error)
+ return None
+
+
+async def cli_client_connected(unet, background, reader, writer):
+ """Handle CLI commands inside the munet process from a socket."""
+ # # Go into full non-blocking mode now
+ # client.settimeout(None)
+ logging.debug("cli client connected")
+ while True:
+ line = await reader.readline()
+ if not line:
+ logging.debug("client closed cli connection")
+ break
+ line = line.decode("utf-8").strip()
+
+ class EncodingFile:
+ """Wrap a writer to encode in utf-8."""
+
+ def __init__(self, writer):
+ self.writer = writer
+
+ def write(self, x):
+ self.writer.write(x.encode("utf-8", "ignore"))
+
+ def flush(self):
+ self.writer.flush()
+
+ if not await doline(unet, line, EncodingFile(writer), background, notty=True):
+ logging.debug("server closing cli connection")
+ return
+
+ writer.write(ENDMARKER)
+ await writer.drain()
+
+
+async def remote_cli(unet, prompt, title, background):
+ """Open a CLI in a new window."""
+ try:
+ if not unet.cli_sockpath:
+ sockpath = os.path.join(tempfile.mkdtemp("-sockdir", "pty-"), "cli.sock")
+ ccfunc = functools.partial(cli_client_connected, unet, background)
+ s = await asyncio.start_unix_server(ccfunc, path=sockpath)
+ unet.cli_server = asyncio.create_task(s.serve_forever(), name="cli-task")
+ unet.cli_sockpath = sockpath
+ logging.info("server created on :\n%s\n", sockpath)
+
+ # Open a new window with a new CLI
+ python_path = await unet.async_get_exec_path(["python3", "python"])
+ us = os.path.realpath(__file__)
+ cmd = f"{python_path} {us}"
+ if unet.cli_histfile:
+ cmd += " --histfile=" + unet.cli_histfile
+ if prompt:
+ cmd += f" --prompt='{prompt}'"
+ cmd += " " + unet.cli_sockpath
+ unet.run_in_window(cmd, title=title, background=False)
+ except Exception as error:
+ logging.error("cli server: unexpected exception: %s", error)
+
+
+def add_cli_in_window_cmd(
+ unet, name, helpfmt, helptxt, execfmt, toplevel, kinds, **kwargs
+):
+ """Adds a CLI command to the CLI.
+
+ The command `cmd` is added to the commands executable by the user from the CLI. See
+ `base.Commander.run_in_window` for the arguments that can be passed in `args` and
+ `kwargs` to this function.
+
+ Args:
+ unet: unet object
+ name: command string (no spaces)
+ helpfmt: format of command to display in help (left side)
+ helptxt: help string for command (right side)
+ execfmt: interpreter `cmd` to pass to `host.run_in_window()`, if {} present then
+ allow for user commands to be entered and inserted. May also be a dict of dict
+ keyed on kind with sub-key of "exec" providing the `execfmt` string for that
+ kind.
+ toplevel: run command in common top-level namespaec not inside hosts
+ kinds: limit CLI command to nodes which match list of kinds.
+ **kwargs: keyword args to pass to `host.run_in_window()`
+ """
+ unet.cli_in_window_cmds[name] = (helpfmt, helptxt, execfmt, toplevel, kinds, kwargs)
+
+
+def add_cli_run_cmd(
+ unet,
+ name,
+ helpfmt,
+ helptxt,
+ execfmt,
+ toplevel,
+ kinds,
+ ns_only=False,
+ interactive=False,
+):
+ """Adds a CLI command to the CLI.
+
+ The command `cmd` is added to the commands executable by the user from the CLI.
+ See `run_command` above in the `doline` function and for the arguments that can
+ be passed in to this function.
+
+ Args:
+ unet: unet object
+ name: command string (no spaces)
+ helpfmt: format of command to display in help (left side)
+ helptxt: help string for command (right side)
+ execfmt: format string to insert user cmds into for execution. May also be a
+ dict of dict keyed on kind with sub-key of "exec" providing the `execfmt`
+ string for that kind.
+ toplevel: run command in common top-level namespaec not inside hosts
+ kinds: limit CLI command to nodes which match list of kinds.
+ ns_only: Should execute the command on the host vs in the node namespace.
+ interactive: Should execute the command inside an allocated pty (interactive)
+ """
+ unet.cli_run_cmds[name] = (
+ helpfmt,
+ helptxt,
+ execfmt,
+ toplevel,
+ kinds,
+ ns_only,
+ interactive,
+ )
+
+
+def add_cli_config(unet, config):
+ """Adds CLI commands based on config.
+
+ All exec strings will have %CONFIGDIR%, %NAME% and %RUNDIR% replaced with the
+ corresponding config directory and the current nodes `name` and `rundir`.
+ Additionally, the exec string will have f-string style interpolation performed
+ with the local variables `host` (node object or None), `unet` (Munet object) and
+ `user_input` (if provided to the CLI command) defined.
+
+ The format of the config dictionary can be seen in the following example.
+ The first list entry represents the default command because it has no `name` key.
+
+ commands:
+ - help: "run the given FRR command using vtysh"
+ format: "[HOST ...] FRR-CLI-COMMAND"
+ exec: "vtysh -c {}"
+ ns-only: false # the default
+ interactive: false # the default
+ - name: "vtysh"
+ help: "Open a FRR CLI inside new terminal[s] on the given HOST[s]"
+ format: "vtysh HOST [HOST ...]"
+ exec: "vtysh"
+ new-window: true
+ - name: "capture"
+ help: "Capture packets on a given network"
+ format: "pcap NETWORK"
+ exec: "tshark -s 9200 -i {0} -w /tmp/capture-{0}.pcap"
+ new-window: true
+ top-level: true # run in top-level container namespace, above hosts
+
+ The `new_window` key can also be a dictionary which will be passed as keyward
+ arguments to the `Commander.run_in_window()` function.
+
+ Args:
+ unet: unet object
+ config: dictionary of cli config
+ """
+ for cli_cmd in config.get("commands", []):
+ name = cli_cmd.get("name", None)
+ helpfmt = cli_cmd.get("format", "")
+ helptxt = cli_cmd.get("help", "")
+ execfmt = list_to_dict_with_key(cli_cmd.get("exec-kind"), "kind")
+ if not execfmt:
+ execfmt = cli_cmd.get("exec", "bash -c '{}'")
+ toplevel = cli_cmd.get("top-level", False)
+ kinds = cli_cmd.get("kinds", [])
+ stdargs = (unet, name, helpfmt, helptxt, execfmt, toplevel, kinds)
+ new_window = cli_cmd.get("new-window", None)
+ if isinstance(new_window, dict):
+ add_cli_in_window_cmd(*stdargs, **new_window)
+ elif bool(new_window):
+ add_cli_in_window_cmd(*stdargs)
+ else:
+ # on-host is deprecated it really implemented "ns-only"
+ add_cli_run_cmd(
+ *stdargs,
+ cli_cmd.get("ns-only", cli_cmd.get("on-host")),
+ cli_cmd.get("interactive", False),
+ )
+
+
+def cli(
+ unet,
+ histfile=None,
+ sockpath=None,
+ force_window=False,
+ title=None,
+ prompt=None,
+ background=True,
+):
+ asyncio.run(
+ async_cli(unet, histfile, sockpath, force_window, title, prompt, background)
+ )
+
+
+async def async_cli(
+ unet,
+ histfile=None,
+ sockpath=None,
+ force_window=False,
+ title=None,
+ prompt=None,
+ background=True,
+):
+ if prompt is None:
+ prompt = "munet> "
+
+ if force_window or not sys.stdin.isatty():
+ await remote_cli(unet, prompt, title, background)
+
+ if not unet:
+ logger.debug("client-cli using sockpath %s", sockpath)
+
+ try:
+ if sockpath:
+ await cli_client(sockpath, prompt)
+ else:
+ await local_cli(unet, sys.stdout, prompt, histfile, background)
+ except KeyboardInterrupt:
+ print("\n...^C exiting CLI")
+ except EOFError:
+ pass
+ except Exception as ex:
+ logger.critical("cli: got exception: %s", ex, exc_info=True)
+ raise
+
+
+if __name__ == "__main__":
+ # logging.basicConfig(level=logging.DEBUG, filename="/tmp/topotests/cli-client.log")
+ logging.basicConfig(level=logging.DEBUG)
+ 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", help="prompt string to use")
+ parser.add_argument("socket", help="path to pair of sockets to communicate over")
+ cli_args = parser.parse_args()
+
+ cli_prompt = cli_args.prompt if cli_args.prompt else "munet> "
+ asyncio.run(
+ async_cli(
+ None,
+ cli_args.histfile,
+ cli_args.socket,
+ prompt=cli_prompt,
+ background=False,
+ )
+ )
diff --git a/tests/topotests/munet/compat.py b/tests/topotests/munet/compat.py
new file mode 100644
index 0000000..e82a7d5
--- /dev/null
+++ b/tests/topotests/munet/compat.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# November 16 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+#
+"""Provide compatible APIs."""
+
+
+class PytestConfig:
+ """Pytest config duck-type-compatible object using argprase args."""
+
+ class Namespace:
+ """A namespace defined by a dictionary of values."""
+
+ def __init__(self, args):
+ self.args = args
+
+ def __getattr__(self, attr):
+ return self.args[attr] if attr in self.args else None
+
+ def __init__(self, args):
+ self.args = vars(args)
+ self.option = PytestConfig.Namespace(self.args)
+
+ def getoption(self, name, default=None, skip=False):
+ assert not skip
+ if name.startswith("--"):
+ name = name[2:]
+ name = name.replace("-", "_")
+ if name in self.args:
+ return self.args[name] if self.args[name] is not None else default
+ return default
diff --git a/tests/topotests/munet/config.py b/tests/topotests/munet/config.py
new file mode 100644
index 0000000..2870ae6
--- /dev/null
+++ b/tests/topotests/munet/config.py
@@ -0,0 +1,213 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# June 25 2022, Christian Hopps <chopps@gmail.com>
+#
+# Copyright (c) 2021-2022, LabN Consulting, L.L.C.
+#
+"""A module that defines common configuration utility functions."""
+import logging
+
+from collections.abc import Iterable
+from copy import deepcopy
+from typing import overload
+
+
+def find_with_kv(lst, k, v):
+ if lst:
+ for e in lst:
+ if k in e and e[k] == v:
+ return e
+ return {}
+
+
+def find_all_with_kv(lst, k, v):
+ rv = []
+ if lst:
+ for e in lst:
+ if k in e and e[k] == v:
+ rv.append(e)
+ return rv
+
+
+def find_matching_net_config(name, cconf, oconf):
+ p = find_all_with_kv(oconf.get("connections", {}), "to", name)
+ if not p:
+ return {}
+
+ rname = cconf.get("remote-name", None)
+ if not rname:
+ return p[0]
+
+ return find_with_kv(p, "name", rname)
+
+
+def merge_using_key(a, b, k):
+ # First get a dict of indexes in `a` for the key value of `k` in objects of `a`
+ m = list(a)
+ mi = {o[k]: i for i, o in enumerate(m)}
+ for o in b:
+ bkv = o[k]
+ if bkv in mi:
+ m[mi[bkv]] = o
+ else:
+ mi[bkv] = len(m)
+ m.append(o)
+ return m
+
+
+def list_to_dict_with_key(lst, k):
+ """Convert a YANG styl list of objects to dict of objects.
+
+ This function converts a YANG style list of objects (dictionaries) to a plain python
+ dictionary of objects (dictionaries). The value for the supplied key for each
+ object is used to store the object in the new diciontary.
+
+ This only works for lists of objects which are keyed on a single contained value.
+
+ Args:
+ lst: a *list* of python dictionary objects.
+ k: the key value contained in each dictionary object in the list.
+
+ Returns:
+ A dictionary of objects (dictionaries).
+ """
+ return {x[k]: x for x in (lst if lst else [])}
+
+
+def config_to_dict_with_key(c, ck, k):
+ """Convert the config item from a list of objects to dict.
+
+ Use :py:func:`list_to_dict_with_key` to convert the list of objects
+ at ``c[ck]`` to a dict of the objects using the key ``k``.
+
+ Args:
+ c: config dictionary
+ ck: The key identifying the list of objects from ``c``.
+ k: The key to pass to :py:func:`list_to_dict_with_key`.
+
+ Returns:
+ A dictionary of objects (dictionaries).
+ """
+ c[ck] = list_to_dict_with_key(c.get(ck, []), k)
+ return c[ck]
+
+
+@overload
+def config_subst(config: str, **kwargs) -> str:
+ ...
+
+
+@overload
+def config_subst(config: Iterable, **kwargs) -> Iterable:
+ ...
+
+
+def config_subst(config: Iterable, **kwargs) -> Iterable:
+ if isinstance(config, str):
+ if "%RUNDIR%/%NAME%" in config:
+ config = config.replace("%RUNDIR%/%NAME%", "%RUNDIR%")
+ logging.warning(
+ "config '%RUNDIR%/%NAME%' should be changed to '%RUNDIR%' only, "
+ "converting automatically for now."
+ )
+ for name, value in kwargs.items():
+ config = config.replace(f"%{name.upper()}%", str(value))
+ elif isinstance(config, Iterable):
+ try:
+ return {k: config_subst(config[k], **kwargs) for k in config}
+ except (KeyError, TypeError):
+ return [config_subst(x, **kwargs) for x in config]
+ return config
+
+
+def value_merge_deepcopy(s1, s2):
+ """Merge values using deepcopy.
+
+ Create a deepcopy of the result of merging the values from dicts ``s1`` and ``s2``.
+ If a key exists in both ``s1`` and ``s2`` the value from ``s2`` is used."
+ """
+ d = {}
+ for k, v in s1.items():
+ if k in s2:
+ d[k] = deepcopy(s2[k])
+ else:
+ d[k] = deepcopy(v)
+ return d
+
+
+def merge_kind_config(kconf, config):
+ mergekeys = kconf.get("merge", [])
+ config = deepcopy(config)
+ new = deepcopy(kconf)
+ for k in new:
+ if k not in config:
+ continue
+
+ if k not in mergekeys:
+ new[k] = config[k]
+ elif isinstance(new[k], list):
+ new[k].extend(config[k])
+ elif isinstance(new[k], dict):
+ new[k] = {**new[k], **config[k]}
+ else:
+ new[k] = config[k]
+ for k in config:
+ if k not in new:
+ new[k] = config[k]
+ return new
+
+
+def cli_opt_list(option_list):
+ if not option_list:
+ return []
+ if isinstance(option_list, str):
+ return [x for x in option_list.split(",") if x]
+ return [x for x in option_list if x]
+
+
+def name_in_cli_opt_str(name, option_list):
+ ol = cli_opt_list(option_list)
+ return name in ol or "all" in ol
+
+
+class ConfigOptionsProxy:
+ """Proxy options object to fill in for any missing pytest config."""
+
+ class DefNoneObject:
+ """An object that returns None for any attribute access."""
+
+ def __getattr__(self, attr):
+ return None
+
+ def __init__(self, pytestconfig=None):
+ if isinstance(pytestconfig, ConfigOptionsProxy):
+ self.config = pytestconfig.config
+ self.option = self.config.option
+ else:
+ self.config = pytestconfig
+ if self.config:
+ self.option = self.config.option
+ else:
+ self.option = ConfigOptionsProxy.DefNoneObject()
+
+ def getoption(self, opt, default=None):
+ if not self.config:
+ return default
+
+ try:
+ value = self.config.getoption(opt)
+ return value if value is not None else default
+ except ValueError:
+ return default
+
+ def get_option(self, opt, default=None):
+ return self.getoption(opt, default)
+
+ def get_option_list(self, opt):
+ value = self.get_option(opt, "")
+ return cli_opt_list(value)
+
+ def name_in_option_list(self, name, opt):
+ optlist = self.get_option_list(opt)
+ return "all" in optlist or name in optlist
diff --git a/tests/topotests/munet/kinds.yaml b/tests/topotests/munet/kinds.yaml
new file mode 100644
index 0000000..0c278d3
--- /dev/null
+++ b/tests/topotests/munet/kinds.yaml
@@ -0,0 +1,84 @@
+version: 1
+kinds:
+ - name: frr
+ cap-add:
+ # Zebra requires these
+ - NET_ADMIN
+ - NET_RAW
+ - SYS_ADMIN
+ - AUDIT_WRITE # needed for ssh pty allocation
+ - name: ceos
+ init: false
+ shell: false
+ merge: ["env"]
+ # Should we cap-drop some of these in privileged mode?
+ # ceos kind is special. munet will add args to /sbin/init for each
+ # environment variable of the form `systemd.setenv=ENVNAME=VALUE` for each
+ # environment varialbe named ENVNAME with a value of `VALUE`. If cmd: is
+ # changed to anything but `/sbin/init` munet will not do this.
+ cmd: /sbin/init
+ privileged: true
+ env:
+ - name: "EOS_PLATFORM"
+ value: "ceoslab"
+ - name: "container"
+ value: "docker"
+ - name: "ETBA"
+ value: "4"
+ - name: "SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT"
+ value: "1"
+ - name: "INTFTYPE"
+ value: "eth"
+ - name: "MAPETH0"
+ value: "1"
+ - name: "MGMT_INTF"
+ value: "eth0"
+ - name: "CEOS"
+ value: "1"
+
+ # cap-add:
+ # # cEOS requires these, except GNMI still doesn't work
+ # # - NET_ADMIN
+ # # - NET_RAW
+ # # - SYS_ADMIN
+ # # - SYS_RESOURCE # Required for the CLI
+
+ # All Caps
+ # - AUDIT_CONTROL
+ # - AUDIT_READ
+ # - AUDIT_WRITE
+ # - BLOCK_SUSPEND
+ # - CHOWN
+ # - DAC_OVERRIDE
+ # - DAC_READ_SEARCH
+ # - FOWNER
+ # - FSETID
+ # - IPC_LOCK
+ # - IPC_OWNER
+ # - KILL
+ # - LEASE
+ # - LINUX_IMMUTABLE
+ # - MKNOD
+ # - NET_ADMIN
+ # - NET_BIND_SERVICE
+ # - NET_BROADCAST
+ # - NET_RAW
+ # - SETFCAP
+ # - SETGID
+ # - SETPCAP
+ # - SETUID
+ # - SYSLOG
+ # - SYS_ADMIN
+ # - SYS_BOOT
+ # - SYS_CHROOT
+ # - SYS_MODULE
+ # - SYS_NICE
+ # - SYS_PACCT
+ # - SYS_PTRACE
+ # - SYS_RAWIO
+ # - SYS_RESOURCE
+ # - SYS_TIME
+ # - SYS_TTY_CONFIG
+ # - WAKE_ALARM
+ # - MAC_ADMIN - Smack project?
+ # - MAC_OVERRIDE - Smack project?
diff --git a/tests/topotests/munet/linux.py b/tests/topotests/munet/linux.py
new file mode 100644
index 0000000..417f745
--- /dev/null
+++ b/tests/topotests/munet/linux.py
@@ -0,0 +1,267 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# June 10 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+#
+"""A module that gives access to linux unshare system call."""
+
+import ctypes # pylint: disable=C0415
+import ctypes.util # pylint: disable=C0415
+import errno
+import functools
+import os
+
+
+libc = None
+
+
+def raise_oserror(enum):
+ s = errno.errorcode[enum] if enum in errno.errorcode else str(enum)
+ error = OSError(s)
+ error.errno = enum
+ error.strerror = s
+ raise error
+
+
+def _load_libc():
+ global libc # pylint: disable=W0601,W0603
+ if libc:
+ return
+ lcpath = ctypes.util.find_library("c")
+ libc = ctypes.CDLL(lcpath, use_errno=True)
+
+
+def pause():
+ if not libc:
+ _load_libc()
+ libc.pause()
+
+
+MS_RDONLY = 1
+MS_NOSUID = 1 << 1
+MS_NODEV = 1 << 2
+MS_NOEXEC = 1 << 3
+MS_SYNCHRONOUS = 1 << 4
+MS_REMOUNT = 1 << 5
+MS_MANDLOCK = 1 << 6
+MS_DIRSYNC = 1 << 7
+MS_NOSYMFOLLOW = 1 << 8
+MS_NOATIME = 1 << 10
+MS_NODIRATIME = 1 << 11
+MS_BIND = 1 << 12
+MS_MOVE = 1 << 13
+MS_REC = 1 << 14
+MS_SILENT = 1 << 15
+MS_POSIXACL = 1 << 16
+MS_UNBINDABLE = 1 << 17
+MS_PRIVATE = 1 << 18
+MS_SLAVE = 1 << 19
+MS_SHARED = 1 << 20
+MS_RELATIME = 1 << 21
+MS_KERNMOUNT = 1 << 22
+MS_I_VERSION = 1 << 23
+MS_STRICTATIME = 1 << 24
+MS_LAZYTIME = 1 << 25
+
+
+def mount(source, target, fs, flags=0, options=""):
+ if not libc:
+ _load_libc()
+ libc.mount.argtypes = (
+ ctypes.c_char_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p,
+ ctypes.c_ulong,
+ ctypes.c_char_p,
+ )
+ fsenc = fs.encode() if fs else None
+ optenc = options.encode() if options else None
+ ret = libc.mount(source.encode(), target.encode(), fsenc, flags, optenc)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(
+ err,
+ f"Error mounting {source} ({fs}) on {target}"
+ f" with options '{options}': {os.strerror(err)}",
+ )
+
+
+# unmout options
+MNT_FORCE = 0x1
+MNT_DETACH = 0x2
+MNT_EXPIRE = 0x4
+UMOUNT_NOFOLLOW = 0x8
+
+
+def umount(target, options):
+ if not libc:
+ _load_libc()
+ libc.umount.argtypes = (ctypes.c_char_p, ctypes.c_uint)
+
+ ret = libc.umount(target.encode(), int(options))
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(
+ err,
+ f"Error umounting {target} with options '{options}': {os.strerror(err)}",
+ )
+
+
+def pidfd_open(pid, flags=0):
+ if hasattr(os, "pidfd_open") and os.pidfd_open is not pidfd_open:
+ return os.pidfd_open(pid, flags) # pylint: disable=no-member
+
+ if not libc:
+ _load_libc()
+
+ try:
+ pfof = libc.pidfd_open
+ except AttributeError:
+ __NR_pidfd_open = 434
+ _pidfd_open = libc.syscall
+ _pidfd_open.restype = ctypes.c_int
+ _pidfd_open.argtypes = ctypes.c_long, ctypes.c_uint, ctypes.c_uint
+ pfof = functools.partial(_pidfd_open, __NR_pidfd_open)
+
+ fd = pfof(int(pid), int(flags))
+ if fd == -1:
+ raise_oserror(ctypes.get_errno())
+
+ return fd
+
+
+if not hasattr(os, "pidfd_open"):
+ os.pidfd_open = pidfd_open
+
+
+def setns(fd, nstype): # noqa: D402
+ """See setns(2) manpage."""
+ if not libc:
+ _load_libc()
+
+ if libc.setns(int(fd), int(nstype)) == -1:
+ raise_oserror(ctypes.get_errno())
+
+
+def unshare(flags): # noqa: D402
+ """See unshare(2) manpage."""
+ if not libc:
+ _load_libc()
+
+ if libc.unshare(int(flags)) == -1:
+ raise_oserror(ctypes.get_errno())
+
+
+CLONE_NEWTIME = 0x00000080
+CLONE_VM = 0x00000100
+CLONE_FS = 0x00000200
+CLONE_FILES = 0x00000400
+CLONE_SIGHAND = 0x00000800
+CLONE_PIDFD = 0x00001000
+CLONE_PTRACE = 0x00002000
+CLONE_VFORK = 0x00004000
+CLONE_PARENT = 0x00008000
+CLONE_THREAD = 0x00010000
+CLONE_NEWNS = 0x00020000
+CLONE_SYSVSEM = 0x00040000
+CLONE_SETTLS = 0x00080000
+CLONE_PARENT_SETTID = 0x00100000
+CLONE_CHILD_CLEARTID = 0x00200000
+CLONE_DETACHED = 0x00400000
+CLONE_UNTRACED = 0x00800000
+CLONE_CHILD_SETTID = 0x01000000
+CLONE_NEWCGROUP = 0x02000000
+CLONE_NEWUTS = 0x04000000
+CLONE_NEWIPC = 0x08000000
+CLONE_NEWUSER = 0x10000000
+CLONE_NEWPID = 0x20000000
+CLONE_NEWNET = 0x40000000
+CLONE_IO = 0x80000000
+
+clone_flag_names = {
+ CLONE_NEWTIME: "CLONE_NEWTIME",
+ CLONE_VM: "CLONE_VM",
+ CLONE_FS: "CLONE_FS",
+ CLONE_FILES: "CLONE_FILES",
+ CLONE_SIGHAND: "CLONE_SIGHAND",
+ CLONE_PIDFD: "CLONE_PIDFD",
+ CLONE_PTRACE: "CLONE_PTRACE",
+ CLONE_VFORK: "CLONE_VFORK",
+ CLONE_PARENT: "CLONE_PARENT",
+ CLONE_THREAD: "CLONE_THREAD",
+ CLONE_NEWNS: "CLONE_NEWNS",
+ CLONE_SYSVSEM: "CLONE_SYSVSEM",
+ CLONE_SETTLS: "CLONE_SETTLS",
+ CLONE_PARENT_SETTID: "CLONE_PARENT_SETTID",
+ CLONE_CHILD_CLEARTID: "CLONE_CHILD_CLEARTID",
+ CLONE_DETACHED: "CLONE_DETACHED",
+ CLONE_UNTRACED: "CLONE_UNTRACED",
+ CLONE_CHILD_SETTID: "CLONE_CHILD_SETTID",
+ CLONE_NEWCGROUP: "CLONE_NEWCGROUP",
+ CLONE_NEWUTS: "CLONE_NEWUTS",
+ CLONE_NEWIPC: "CLONE_NEWIPC",
+ CLONE_NEWUSER: "CLONE_NEWUSER",
+ CLONE_NEWPID: "CLONE_NEWPID",
+ CLONE_NEWNET: "CLONE_NEWNET",
+ CLONE_IO: "CLONE_IO",
+}
+
+
+def clone_flag_string(flags):
+ ns = [v for k, v in clone_flag_names.items() if k & flags]
+ if ns:
+ return "|".join(ns)
+ return "None"
+
+
+namespace_files = {
+ CLONE_NEWUSER: "ns/user",
+ CLONE_NEWCGROUP: "ns/cgroup",
+ CLONE_NEWIPC: "ns/ipc",
+ CLONE_NEWUTS: "ns/uts",
+ CLONE_NEWNET: "ns/net",
+ CLONE_NEWPID: "ns/pid_for_children",
+ CLONE_NEWNS: "ns/mnt",
+ CLONE_NEWTIME: "ns/time_for_children",
+}
+
+PR_SET_PDEATHSIG = 1
+PR_GET_PDEATHSIG = 2
+PR_SET_NAME = 15
+PR_GET_NAME = 16
+
+
+def set_process_name(name):
+ if not libc:
+ _load_libc()
+
+ # Why does uncommenting this cause failure?
+ # libc.prctl.argtypes = (
+ # ctypes.c_int,
+ # ctypes.c_ulong,
+ # ctypes.c_ulong,
+ # ctypes.c_ulong,
+ # ctypes.c_ulong,
+ # )
+
+ s = ctypes.create_string_buffer(bytes(name, encoding="ascii"))
+ sr = ctypes.byref(s)
+ libc.prctl(PR_SET_NAME, sr, 0, 0, 0)
+
+
+def set_parent_death_signal(signum):
+ if not libc:
+ _load_libc()
+
+ # Why does uncommenting this cause failure?
+ libc.prctl.argtypes = (
+ ctypes.c_int,
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ )
+
+ libc.prctl(PR_SET_PDEATHSIG, signum, 0, 0, 0)
diff --git a/tests/topotests/munet/logconf-mutest.yaml b/tests/topotests/munet/logconf-mutest.yaml
new file mode 100644
index 0000000..b450fb9
--- /dev/null
+++ b/tests/topotests/munet/logconf-mutest.yaml
@@ -0,0 +1,84 @@
+version: 1
+formatters:
+ brief:
+ format: '%(levelname)5s: %(message)s'
+ operfmt:
+ class: munet.mulog.ColorFormatter
+ format: ' ------| %(message)s'
+ exec:
+ format: '%(asctime)s %(levelname)5s: %(name)s: %(message)s'
+ output:
+ format: '%(asctime)s %(levelname)5s: OUTPUT: %(message)s'
+ results:
+ # format: '%(asctime)s %(levelname)5s: %(message)s'
+ format: '%(message)s'
+
+handlers:
+ console:
+ level: WARNING
+ class: logging.StreamHandler
+ formatter: brief
+ stream: ext://sys.stderr
+ info_console:
+ level: INFO
+ class: logging.StreamHandler
+ formatter: brief
+ stream: ext://sys.stderr
+ oper_console:
+ level: DEBUG
+ class: logging.StreamHandler
+ formatter: operfmt
+ stream: ext://sys.stderr
+ exec:
+ level: DEBUG
+ class: logging.FileHandler
+ formatter: exec
+ filename: mutest-exec.log
+ mode: w
+ output:
+ level: DEBUG
+ class: munet.mulog.MultiFileHandler
+ root_path: "mutest.output"
+ formatter: output
+ filename: mutest-output.log
+ mode: w
+ results:
+ level: INFO
+ class: munet.mulog.MultiFileHandler
+ root_path: "mutest.results"
+ new_handler_level: DEBUG
+ formatter: results
+ filename: mutest-results.log
+ mode: w
+
+root:
+ level: DEBUG
+ handlers: [ "console", "exec" ]
+
+loggers:
+ # These are some loggers that get used...
+ # munet:
+ # level: DEBUG
+ # propagate: true
+ # munet.base.commander
+ # level: DEBUG
+ # propagate: true
+ # mutest.error:
+ # level: DEBUG
+ # propagate: true
+ mutest.output:
+ level: DEBUG
+ handlers: ["output", "exec"]
+ propagate: false
+ mutest.results:
+ level: DEBUG
+ handlers: [ "info_console", "exec", "output", "results" ]
+ # We don't propagate this b/c we want a lower level accept on the console
+ # Instead we use info_console and exec to cover what root would log to.
+ propagate: false
+ # This is used to debug the operation of mutest
+ mutest.oper:
+ # Records are emitted at DEBUG so this will normally filter everything
+ level: INFO
+ handlers: [ "oper_console" ]
+ propagate: false
diff --git a/tests/topotests/munet/logconf.yaml b/tests/topotests/munet/logconf.yaml
new file mode 100644
index 0000000..430ee20
--- /dev/null
+++ b/tests/topotests/munet/logconf.yaml
@@ -0,0 +1,32 @@
+version: 1
+formatters:
+ brief:
+ format: '%(asctime)s: %(levelname)s: %(message)s'
+ precise:
+ format: '%(asctime)s %(levelname)s: %(name)s: %(message)s'
+
+handlers:
+ console:
+ class: logging.StreamHandler
+ formatter: brief
+ level: INFO
+ stream: ext://sys.stderr
+ file:
+ class: logging.FileHandler
+ formatter: precise
+ level: DEBUG
+ filename: munet-exec.log
+ mode: w
+
+root:
+ level: DEBUG
+ handlers: [ "console", "file" ]
+
+# these are some loggers that get used.
+# loggers:
+# munet:
+# level: DEBUG
+# propagate: true
+# munet.base.commander
+# level: DEBUG
+# propagate: true
diff --git a/tests/topotests/munet/mucmd.py b/tests/topotests/munet/mucmd.py
new file mode 100644
index 0000000..5518c6d
--- /dev/null
+++ b/tests/topotests/munet/mucmd.py
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# December 5 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""A command that allows external command execution inside nodes."""
+import argparse
+import json
+import os
+import subprocess
+import sys
+
+from pathlib import Path
+
+
+def newest_file_in(filename, paths, has_sibling=None):
+ new = None
+ newst = None
+ items = (x for y in paths for x in Path(y).rglob(filename))
+ for e in items:
+ st = os.stat(e)
+ if has_sibling and not e.parent.joinpath(has_sibling).exists():
+ continue
+ if not new or st.st_mtime_ns > newst.st_mtime_ns:
+ new = e
+ newst = st
+ continue
+ return new, newst
+
+
+def main(*args):
+ ap = argparse.ArgumentParser(args)
+ ap.add_argument("-d", "--rundir", help="runtime directory for tempfiles, logs, etc")
+ ap.add_argument("node", nargs="?", help="node to enter or run command inside")
+ ap.add_argument(
+ "shellcmd",
+ nargs=argparse.REMAINDER,
+ help="optional shell-command to execute on NODE",
+ )
+ args = ap.parse_args()
+ if args.rundir:
+ configpath = Path(args.rundir).joinpath("config.json")
+ else:
+ configpath, _ = newest_file_in(
+ "config.json",
+ ["/tmp/munet", "/tmp/mutest", "/tmp/unet-test"],
+ has_sibling=args.node,
+ )
+ print(f'Using "{configpath}"')
+
+ if not configpath.exists():
+ print(f'"{configpath}" not found')
+ return 1
+ rundir = configpath.parent
+
+ nodes = []
+ config = json.load(open(configpath, encoding="utf-8"))
+ nodes = list(config.get("topology", {}).get("nodes", []))
+ envcfg = config.get("mucmd", {}).get("env", {})
+
+ # If args.node is not a node it's part of shellcmd
+ if args.node and args.node not in nodes:
+ if args.node != ".":
+ args.shellcmd[0:0] = [args.node]
+ args.node = None
+
+ if args.node:
+ name = args.node
+ nodedir = rundir.joinpath(name)
+ if not nodedir.exists():
+ print('"{name}" node doesn\'t exist in "{rundir}"')
+ return 1
+ rundir = nodedir
+ else:
+ name = "munet"
+ pidpath = rundir.joinpath("nspid")
+ pid = open(pidpath, encoding="ascii").read().strip()
+
+ env = {**os.environ}
+ env["MUNET_NODENAME"] = name
+ env["MUNET_RUNDIR"] = str(rundir)
+
+ for k in envcfg:
+ envcfg[k] = envcfg[k].replace("%NAME%", str(name))
+ envcfg[k] = envcfg[k].replace("%RUNDIR%", str(rundir))
+
+ # Can't use -F if it's a new pid namespace
+ ecmd = "/usr/bin/nsenter"
+ eargs = [ecmd]
+
+ output = subprocess.check_output(["/usr/bin/nsenter", "--help"], encoding="utf-8")
+ if " -a," in output:
+ eargs.append("-a")
+ else:
+ # -U doesn't work
+ for flag in ["-u", "-i", "-m", "-n", "-C", "-T"]:
+ if f" {flag}," in output:
+ eargs.append(flag)
+ eargs.append(f"--pid=/proc/{pid}/ns/pid_for_children")
+ eargs.append(f"--wd={rundir}")
+ eargs.extend(["-t", pid])
+ eargs += args.shellcmd
+ # print("Using ", eargs)
+ return os.execvpe(ecmd, eargs, {**env, **envcfg})
+
+
+if __name__ == "__main__":
+ exit_status = main()
+ sys.exit(exit_status)
diff --git a/tests/topotests/munet/mulog.py b/tests/topotests/munet/mulog.py
new file mode 100644
index 0000000..f840eae
--- /dev/null
+++ b/tests/topotests/munet/mulog.py
@@ -0,0 +1,122 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# December 4 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+#
+"""Utilities for logging in munet."""
+
+import logging
+
+from pathlib import Path
+
+
+class MultiFileHandler(logging.FileHandler):
+ """A logging handler that logs to new files based on the logger name.
+
+ The MultiFileHandler operates as a FileHandler with additional functionality. In
+ addition to logging to the specified logging file MultiFileHandler also creates new
+ FileHandlers for child loggers based on a root logging name path.
+
+ The ``root_path`` determines when to create a new FileHandler. For each received log
+ record, ``root_path`` is removed from the logger name of the record if present, and
+ the resulting channel path (if any) determines the directory for a new log file to
+ also emit the record to. The new file path is constructed by starting with the
+ directory ``filename`` resides in, then joining the path determined above after
+ converting "." to "/" and finally by adding back the basename of ``filename``.
+
+ record logger path => mutest.output.testingfoo
+ root_path => mutest.output
+ base filename => /tmp/mutest/mutest-exec.log
+ new logfile => /tmp/mutest/testingfoo/mutest-exec.log
+
+ All messages are also emitted to the common FileLogger for ``filename``.
+
+ If a log record is from a logger that does not start with ``root_path`` no file is
+ created and the normal emit occurs.
+
+ Args:
+ root_path: the logging path of the root level for this handler.
+ new_handler_level: logging level for newly created handlers
+ log_dir: the log directory to put log files in.
+ filename: the base log file.
+ """
+
+ def __init__(self, root_path, filename=None, **kwargs):
+ self.__root_path = root_path
+ self.__basename = Path(filename).name
+ if root_path[-1] != ".":
+ self.__root_path += "."
+ self.__root_pathlen = len(self.__root_path)
+ self.__kwargs = kwargs
+ self.__log_dir = Path(filename).absolute().parent
+ self.__log_dir.mkdir(parents=True, exist_ok=True)
+ self.__filenames = {}
+ self.__added = set()
+
+ if "new_handler_level" not in kwargs:
+ self.__new_handler_level = logging.NOTSET
+ else:
+ new_handler_level = kwargs["new_handler_level"]
+ del kwargs["new_handler_level"]
+ self.__new_handler_level = new_handler_level
+
+ super().__init__(filename=filename, **kwargs)
+
+ if self.__new_handler_level is None:
+ self.__new_handler_level = self.level
+
+ def __log_filename(self, name):
+ if name in self.__filenames:
+ return self.__filenames[name]
+
+ if not name.startswith(self.__root_path):
+ newname = None
+ else:
+ newname = name[self.__root_pathlen :]
+ newname = Path(newname.replace(".", "/"))
+ newname = self.__log_dir.joinpath(newname)
+ newname = newname.joinpath(self.__basename)
+ self.__filenames[name] = newname
+
+ self.__filenames[name] = newname
+ return newname
+
+ def emit(self, record):
+ newname = self.__log_filename(record.name)
+ if newname:
+ if newname not in self.__added:
+ self.__added.add(newname)
+ h = logging.FileHandler(filename=newname, **self.__kwargs)
+ h.setLevel(self.__new_handler_level)
+ h.setFormatter(self.formatter)
+ logging.getLogger(record.name).addHandler(h)
+ h.emit(record)
+ super().emit(record)
+
+
+class ColorFormatter(logging.Formatter):
+ """A formatter that adds color sequences based on level."""
+
+ def __init__(self, fmt=None, datefmt=None, style="%", **kwargs):
+ grey = "\x1b[90m"
+ yellow = "\x1b[33m"
+ red = "\x1b[31m"
+ bold_red = "\x1b[31;1m"
+ reset = "\x1b[0m"
+ # basefmt = " ------| %(message)s "
+
+ self.formatters = {
+ logging.DEBUG: logging.Formatter(grey + fmt + reset),
+ logging.INFO: logging.Formatter(grey + fmt + reset),
+ logging.WARNING: logging.Formatter(yellow + fmt + reset),
+ logging.ERROR: logging.Formatter(red + fmt + reset),
+ logging.CRITICAL: logging.Formatter(bold_red + fmt + reset),
+ }
+ # Why are we even bothering?
+ super().__init__(fmt, datefmt, style, **kwargs)
+
+ def format(self, record):
+ formatter = self.formatters.get(record.levelno)
+ return formatter.format(record)
diff --git a/tests/topotests/munet/munet-schema.json b/tests/topotests/munet/munet-schema.json
new file mode 100644
index 0000000..a1dcd87
--- /dev/null
+++ b/tests/topotests/munet/munet-schema.json
@@ -0,0 +1,654 @@
+{
+ "title": "labn-munet-config",
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "description": "Generated by pyang from module labn-munet-config",
+ "type": "object",
+ "properties": {
+ "cli": {
+ "type": "object",
+ "properties": {
+ "commands": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "exec": {
+ "type": "string"
+ },
+ "exec-kind": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "kind": {
+ "type": "string"
+ },
+ "exec": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "format": {
+ "type": "string"
+ },
+ "help": {
+ "type": "string"
+ },
+ "interactive": {
+ "type": "boolean"
+ },
+ "kinds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "name": {
+ "type": "string"
+ },
+ "new-window": {
+ "type": "boolean"
+ },
+ "top-level": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+ },
+ "kinds": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "merge": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "cap-add": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "cap-remove": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "cmd": {
+ "type": "string"
+ },
+ "cleanup-cmd": {
+ "type": "string"
+ },
+ "ready-cmd": {
+ "type": "string"
+ },
+ "image": {
+ "type": "string"
+ },
+ "server": {
+ "type": "string"
+ },
+ "server-port": {
+ "type": "number"
+ },
+ "qemu": {
+ "type": "object",
+ "properties": {
+ "bios": {
+ "type": "string"
+ },
+ "disk": {
+ "type": "string"
+ },
+ "kerenel": {
+ "type": "string"
+ },
+ "initrd": {
+ "type": "string"
+ },
+ "kvm": {
+ "type": "boolean"
+ },
+ "ncpu": {
+ "type": "integer"
+ },
+ "memory": {
+ "type": "string"
+ },
+ "root": {
+ "type": "string"
+ },
+ "cmdline-extra": {
+ "type": "string"
+ },
+ "extra-args": {
+ "type": "string"
+ },
+ "console": {
+ "type": "object",
+ "properties": {
+ "user": {
+ "type": "string"
+ },
+ "password": {
+ "type": "string"
+ },
+ "expects": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "sends": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "timeout": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "connections": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "to": {
+ "type": "string"
+ },
+ "ip": {
+ "type": "string"
+ },
+ "ipv6": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "hostintf": {
+ "type": "string"
+ },
+ "physical": {
+ "type": "string"
+ },
+ "remote-name": {
+ "type": "string"
+ },
+ "driver": {
+ "type": "string"
+ },
+ "delay": {
+ "type": "integer"
+ },
+ "jitter": {
+ "type": "integer"
+ },
+ "jitter-correlation": {
+ "type": "string"
+ },
+ "loss": {
+ "type": "integer"
+ },
+ "loss-correlation": {
+ "type": "string"
+ },
+ "rate": {
+ "type": "object",
+ "properties": {
+ "rate": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "limit": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "burst": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "string"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "env": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "gdb-cmd": {
+ "type": "string"
+ },
+ "gdb-target-cmds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "gdb-run-cmds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "init": {
+ "oneOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "mounts": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "destination": {
+ "type": "string"
+ },
+ "source": {
+ "type": "string"
+ },
+ "tmpfs-size": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "name": {
+ "type": "string"
+ },
+ "podman": {
+ "type": "object",
+ "properties": {
+ "extra-args": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "privileged": {
+ "type": "boolean"
+ },
+ "shell": {
+ "oneOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "volumes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "topology": {
+ "type": "object",
+ "properties": {
+ "dns-network": {
+ "type": "string"
+ },
+ "ipv6-enable": {
+ "type": "boolean"
+ },
+ "networks-autonumber": {
+ "type": "boolean"
+ },
+ "networks": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "ip": {
+ "type": "string"
+ },
+ "ipv6": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "nodes": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "kind": {
+ "type": "string"
+ },
+ "cap-add": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "cap-remove": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "cmd": {
+ "type": "string"
+ },
+ "cleanup-cmd": {
+ "type": "string"
+ },
+ "ready-cmd": {
+ "type": "string"
+ },
+ "image": {
+ "type": "string"
+ },
+ "server": {
+ "type": "string"
+ },
+ "server-port": {
+ "type": "number"
+ },
+ "qemu": {
+ "type": "object",
+ "properties": {
+ "bios": {
+ "type": "string"
+ },
+ "disk": {
+ "type": "string"
+ },
+ "kerenel": {
+ "type": "string"
+ },
+ "initrd": {
+ "type": "string"
+ },
+ "kvm": {
+ "type": "boolean"
+ },
+ "ncpu": {
+ "type": "integer"
+ },
+ "memory": {
+ "type": "string"
+ },
+ "root": {
+ "type": "string"
+ },
+ "cmdline-extra": {
+ "type": "string"
+ },
+ "extra-args": {
+ "type": "string"
+ },
+ "console": {
+ "type": "object",
+ "properties": {
+ "user": {
+ "type": "string"
+ },
+ "password": {
+ "type": "string"
+ },
+ "expects": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "sends": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "timeout": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "connections": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "to": {
+ "type": "string"
+ },
+ "ip": {
+ "type": "string"
+ },
+ "ipv6": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "hostintf": {
+ "type": "string"
+ },
+ "physical": {
+ "type": "string"
+ },
+ "remote-name": {
+ "type": "string"
+ },
+ "driver": {
+ "type": "string"
+ },
+ "delay": {
+ "type": "integer"
+ },
+ "jitter": {
+ "type": "integer"
+ },
+ "jitter-correlation": {
+ "type": "string"
+ },
+ "loss": {
+ "type": "integer"
+ },
+ "loss-correlation": {
+ "type": "string"
+ },
+ "rate": {
+ "type": "object",
+ "properties": {
+ "rate": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "limit": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "burst": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "string"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "env": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "gdb-cmd": {
+ "type": "string"
+ },
+ "gdb-target-cmds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "gdb-run-cmds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "init": {
+ "oneOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "mounts": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "destination": {
+ "type": "string"
+ },
+ "source": {
+ "type": "string"
+ },
+ "tmpfs-size": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "name": {
+ "type": "string"
+ },
+ "podman": {
+ "type": "object",
+ "properties": {
+ "extra-args": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "privileged": {
+ "type": "boolean"
+ },
+ "shell": {
+ "oneOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "volumes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "version": {
+ "type": "integer"
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/munet/mutest/__main__.py b/tests/topotests/munet/mutest/__main__.py
new file mode 100644
index 0000000..c870311
--- /dev/null
+++ b/tests/topotests/munet/mutest/__main__.py
@@ -0,0 +1,445 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# December 2 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+#
+"""Command to execute mutests."""
+
+import asyncio
+import logging
+import os
+import subprocess
+import sys
+import time
+
+from argparse import ArgumentParser
+from argparse import Namespace
+from copy import deepcopy
+from pathlib import Path
+from typing import Union
+
+from munet import parser
+from munet.base import Bridge
+from munet.base import get_event_loop
+from munet.mutest import userapi as uapi
+from munet.native import L3NodeMixin
+from munet.native import Munet
+from munet.parser import async_build_topology
+from munet.parser import get_config
+
+
+# We want all but critical to fit in 5 characters for alignment
+logging.addLevelName(logging.WARNING, "WARN")
+root_logger = logging.getLogger("")
+exec_formatter = logging.Formatter("%(asctime)s %(levelname)5s: %(name)s: %(message)s")
+
+
+async def get_unet(config: dict, croot: Path, rundir: Path, unshare: bool = False):
+ """Create and run a new Munet topology.
+
+ The topology is built from the given ``config`` to run inside the path indicated
+ by ``rundir``. If ``unshare`` is True then the process will unshare into it's
+ own private namespace.
+
+ Args:
+ config: a config dictionary obtained from ``munet.parser.get_config``. This
+ value will be modified and stored in the built ``Munet`` object.
+ croot: common root of all tests, used to search for ``kinds.yaml`` files.
+ rundir: the path to the run directory for this topology.
+ unshare: True to unshare the process into it's own private namespace.
+
+ Yields:
+ Munet: The constructed and running topology.
+ """
+ tasks = []
+ unet = None
+ try:
+ try:
+ unet = await async_build_topology(
+ config, rundir=str(rundir), unshare_inline=unshare
+ )
+ except Exception as error:
+ logging.debug("unet build failed: %s", error, exc_info=True)
+ raise
+ try:
+ tasks = await unet.run()
+ except Exception as error:
+ logging.debug("unet run failed: %s", error, exc_info=True)
+ raise
+ logging.debug("unet topology running")
+ try:
+ yield unet
+ except Exception as error:
+ logging.error("unet fixture: yield unet unexpected exception: %s", error)
+ raise
+ except KeyboardInterrupt:
+ logging.info("Received keyboard while building topology")
+ raise
+ finally:
+ if unet:
+ await unet.async_delete()
+
+ # No one ever awaits these so cancel them
+ logging.debug("unet fixture: cleanup")
+ for task in tasks:
+ task.cancel()
+
+ # Reset the class variables so auto number is predictable
+ logging.debug("unet fixture: resetting ords to 1")
+ L3NodeMixin.next_ord = 1
+ Bridge.next_ord = 1
+
+
+def common_root(path1: Union[str, Path], path2: Union[str, Path]) -> Path:
+ """Find the common root between 2 paths.
+
+ Args:
+ path1: Path
+ path2: Path
+ Returns:
+ Path: the shared root components between ``path1`` and ``path2``.
+
+ Examples:
+ >>> common_root("/foo/bar/baz", "/foo/bar/zip/zap")
+ PosixPath('/foo/bar')
+ >>> common_root("/foo/bar/baz", "/fod/bar/zip/zap")
+ PosixPath('/')
+ """
+ apath1 = Path(path1).absolute().parts
+ apath2 = Path(path2).absolute().parts
+ alen = min(len(apath1), len(apath2))
+ common = None
+ for a, b in zip(apath1[:alen], apath2[:alen]):
+ if a != b:
+ break
+ common = common.joinpath(a) if common else Path(a)
+ return common
+
+
+async def collect(args: Namespace):
+ """Collect test files.
+
+ Files must match the pattern ``mutest_*.py``, and their containing
+ directory must have a munet config file present. This function also changes
+ the current directory to the common parent of all the tests, and paths are
+ returned relative to the common directory.
+
+ Args:
+ args: argparse results
+
+ Returns:
+ (commondir, tests, configs): where ``commondir`` is the path representing
+ the common parent directory of all the testsd, ``tests`` is a
+ dictionary of lists of test files, keyed on their containing directory
+ path, and ``configs`` is a dictionary of config dictionaries also keyed
+ on its containing directory path. The directory paths are relative to a
+ common ancestor.
+ """
+ file_select = args.file_select
+ upaths = args.paths if args.paths else ["."]
+ globpaths = set()
+ for upath in (Path(x) for x in upaths):
+ if upath.is_file():
+ paths = {upath.absolute()}
+ else:
+ paths = {x.absolute() for x in Path(upath).rglob(file_select)}
+ globpaths |= paths
+ tests = {}
+ configs = {}
+
+ # Find the common root
+ # We don't actually need this anymore, the idea was prefix test names
+ # with uncommon paths elements to automatically differentiate them.
+ common = None
+ sortedpaths = []
+ for path in sorted(globpaths):
+ sortedpaths.append(path)
+ dirpath = path.parent
+ common = common_root(common, dirpath) if common else dirpath
+
+ ocwd = Path().absolute()
+ try:
+ os.chdir(common)
+ # Work with relative paths to the common directory
+ for path in (x.relative_to(common) for x in sortedpaths):
+ dirpath = path.parent
+ if dirpath not in configs:
+ try:
+ configs[dirpath] = get_config(search=[dirpath])
+ except FileNotFoundError:
+ logging.warning(
+ "Skipping '%s' as munet.{yaml,toml,json} not found in '%s'",
+ path,
+ dirpath,
+ )
+ continue
+ if dirpath not in tests:
+ tests[dirpath] = []
+ tests[dirpath].append(path.absolute())
+ finally:
+ os.chdir(ocwd)
+ return common, tests, configs
+
+
+async def execute_test(
+ unet: Munet,
+ test: Path,
+ args: Namespace,
+ test_num: int,
+ exec_handler: logging.Handler,
+) -> (int, int, int, Exception):
+ """Execute a test case script.
+
+ Using the built and running topology in ``unet`` for targets
+ execute the test case script file ``test``.
+
+ Args:
+ unet: a running topology.
+ test: path to the test case script file.
+ args: argparse results.
+ test_num: the number of this test case in the run.
+ exec_handler: exec file handler to add to test loggers which do not propagate.
+ """
+ test_name = testname_from_path(test)
+
+ # Get test case loggers
+ logger = logging.getLogger(f"mutest.output.{test_name}")
+ reslog = logging.getLogger(f"mutest.results.{test_name}")
+ logger.addHandler(exec_handler)
+ reslog.addHandler(exec_handler)
+
+ # We need to send an info level log to cause the speciifc handler to be
+ # created, otherwise all these debug ones don't get through
+ reslog.info("")
+
+ # reslog.debug("START: %s:%s from %s", test_num, test_name, test.stem)
+ # reslog.debug("-" * 70)
+
+ targets = dict(unet.hosts.items())
+ targets["."] = unet
+
+ tc = uapi.TestCase(
+ str(test_num), test_name, test, targets, logger, reslog, args.full_summary
+ )
+ passed, failed, e = tc.execute()
+
+ run_time = time.time() - tc.info.start_time
+
+ status = "PASS" if not (failed or e) else "FAIL"
+
+ # Turn off for now
+ reslog.debug("-" * 70)
+ reslog.debug(
+ "stats: %d steps, %d pass, %d fail, %s abort, %4.2fs elapsed",
+ passed + failed,
+ passed,
+ failed,
+ 1 if e else 0,
+ run_time,
+ )
+ reslog.debug("-" * 70)
+ reslog.debug("END: %s %s:%s\n", status, test_num, test_name)
+
+ return passed, failed, e
+
+
+def testname_from_path(path: Path) -> str:
+ """Return test name based on the path to the test file.
+
+ Args:
+ path: path to the test file.
+
+ Returns:
+ str: the name of the test.
+ """
+ return str(Path(path).stem).replace("/", ".")
+
+
+def print_header(reslog, unet):
+ targets = dict(unet.hosts.items())
+ nmax = max(len(x) for x in targets)
+ nmax = max(nmax, len("TARGET"))
+ sum_fmt = uapi.TestCase.sum_fmt.format(nmax)
+ reslog.info(sum_fmt, "NUMBER", "STAT", "TARGET", "TIME", "DESCRIPTION")
+ reslog.info("-" * 70)
+
+
+async def run_tests(args):
+ reslog = logging.getLogger("mutest.results")
+
+ common, tests, configs = await collect(args)
+ results = []
+ errlog = logging.getLogger("mutest.error")
+ reslog = logging.getLogger("mutest.results")
+ printed_header = False
+ tnum = 0
+ start_time = time.time()
+ try:
+ for dirpath in tests:
+ test_files = tests[dirpath]
+ for test in test_files:
+ tnum += 1
+ config = deepcopy(configs[dirpath])
+ test_name = testname_from_path(test)
+ rundir = args.rundir.joinpath(test_name)
+
+ # Add an test case exec file handler to the root logger and result
+ # logger
+ exec_path = rundir.joinpath("mutest-exec.log")
+ exec_path.parent.mkdir(parents=True, exist_ok=True)
+ exec_handler = logging.FileHandler(exec_path, "w")
+ exec_handler.setFormatter(exec_formatter)
+ root_logger.addHandler(exec_handler)
+
+ try:
+ async for unet in get_unet(config, common, rundir):
+ if not printed_header:
+ print_header(reslog, unet)
+ printed_header = True
+ passed, failed, e = await execute_test(
+ unet, test, args, tnum, exec_handler
+ )
+ except KeyboardInterrupt as error:
+ errlog.warning("KeyboardInterrupt while running test %s", test_name)
+ passed, failed, e = 0, 0, error
+ raise
+ except Exception as error:
+ logging.error(
+ "Error executing test %s: %s", test, error, exc_info=True
+ )
+ errlog.error(
+ "Error executing test %s: %s", test, error, exc_info=True
+ )
+ passed, failed, e = 0, 0, error
+ finally:
+ # Remove the test case exec file handler form the root logger.
+ root_logger.removeHandler(exec_handler)
+ results.append((test_name, passed, failed, e))
+
+ except KeyboardInterrupt:
+ pass
+
+ run_time = time.time() - start_time
+ tnum = 0
+ tpassed = 0
+ tfailed = 0
+ texc = 0
+
+ spassed = 0
+ sfailed = 0
+
+ for result in results:
+ _, passed, failed, e = result
+ tnum += 1
+ spassed += passed
+ sfailed += failed
+ if e:
+ texc += 1
+ if failed or e:
+ tfailed += 1
+ else:
+ tpassed += 1
+
+ reslog.info("")
+ reslog.info(
+ "run stats: %s steps, %s pass, %s fail, %s abort, %4.2fs elapsed",
+ spassed + sfailed,
+ spassed,
+ sfailed,
+ texc,
+ run_time,
+ )
+ reslog.info("-" * 70)
+
+ tnum = 0
+ for result in results:
+ test_name, passed, failed, e = result
+ tnum += 1
+ s = "FAIL" if failed or e else "PASS"
+ reslog.info(" %s %s:%s", s, tnum, test_name)
+
+ reslog.info("-" * 70)
+ reslog.info(
+ "END RUN: %s test scripts, %s passed, %s failed", tnum, tpassed, tfailed
+ )
+
+ return 1 if tfailed else 0
+
+
+async def async_main(args):
+ status = 3
+ try:
+ # For some reson we are not catching exceptions raised inside
+ status = await run_tests(args)
+ except KeyboardInterrupt:
+ logging.info("Exiting (async_main), received KeyboardInterrupt in main")
+ except Exception as error:
+ logging.info(
+ "Exiting (async_main), unexpected exception %s", error, exc_info=True
+ )
+ logging.debug("async_main returns %s", status)
+ return status
+
+
+def main():
+ ap = ArgumentParser()
+ ap.add_argument(
+ "--dist",
+ type=int,
+ nargs="?",
+ const=-1,
+ default=0,
+ action="store",
+ metavar="NUM-THREADS",
+ help="Run in parallel, value is num. of threads or no value for auto",
+ )
+ ap.add_argument("-d", "--rundir", help="runtime directory for tempfiles, logs, etc")
+ ap.add_argument(
+ "--file-select", default="mutest_*.py", help="shell glob for finding tests"
+ )
+ ap.add_argument("--log-config", help="logging config file (yaml, toml, json, ...)")
+ ap.add_argument(
+ "-V",
+ "--full-summary",
+ action="store_true",
+ help="print full summary headers from docstrings",
+ )
+ ap.add_argument(
+ "-v", dest="verbose", action="count", default=0, help="More -v's, more verbose"
+ )
+ ap.add_argument("paths", nargs="*", help="Paths to collect tests from")
+ args = ap.parse_args()
+
+ rundir = args.rundir if args.rundir else "/tmp/mutest"
+ args.rundir = Path(rundir)
+ os.environ["MUNET_RUNDIR"] = rundir
+ subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True)
+
+ config = parser.setup_logging(args, config_base="logconf-mutest")
+ # Grab the exec formatter from the logging config
+ if fconfig := config.get("formatters", {}).get("exec"):
+ global exec_formatter # pylint: disable=W291,W0603
+ exec_formatter = logging.Formatter(
+ fconfig.get("format"), fconfig.get("datefmt")
+ )
+
+ loop = None
+ status = 4
+ try:
+ loop = get_event_loop()
+ status = loop.run_until_complete(async_main(args))
+ except KeyboardInterrupt:
+ logging.info("Exiting (main), received KeyboardInterrupt in main")
+ except Exception as error:
+ logging.info("Exiting (main), unexpected exception %s", error, exc_info=True)
+ finally:
+ if loop:
+ loop.close()
+
+ sys.exit(status)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/topotests/munet/mutest/userapi.py b/tests/topotests/munet/mutest/userapi.py
new file mode 100644
index 0000000..1df8c0d
--- /dev/null
+++ b/tests/topotests/munet/mutest/userapi.py
@@ -0,0 +1,1111 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright 2017, 2022, LabN Consulting, L.L.C.
+"""Mutest is a simple send/expect based testing framework.
+
+This module implements the basic send/expect functionality for mutest. The test
+developer first creates a munet topology (:ref:`munet-config`) and then writes test
+scripts ("test cases") which are composed of calls to the functions defined below
+("steps"). In short these are:
+
+Send/Expect functions:
+
+ - :py:func:`step`
+
+ - :py:func:`step_json`
+
+ - :py:func:`match_step`
+
+ - :py:func:`match_step_json`
+
+ - :py:func:`wait_step`
+
+ - :py:func:`wait_step_json`
+
+Control/Utility functions:
+
+ - :py:func:`script_dir`
+
+ - :py:func:`include`
+
+ - :py:func:`log`
+
+ - :py:func:`test`
+
+Test scripts are located by the :command:`mutest` command by their name. The name of a
+test script should take the form ``mutest_TESTNAME.py`` where ``TESTNAME`` is replaced
+with a user chosen name for the test case.
+
+Here's a simple example test script which first checks that a specific forwarding entry
+is in the FIB for the IP destination ``10.0.1.1``. Then it checks repeatedly for up to
+10 seconds for a second forwarding entry in the FIB for the IP destination ``10.0.2.1``.
+
+.. code-block:: python
+
+ match_step("r1", 'vtysh -c "show ip fib 10.0.1.1"', "Routing entry for 10.0.1.0/24",
+ "Check for FIB entry for 10.0.1.1")
+
+ wait_step("r1",
+ 'vtysh -c "show ip fib 10.0.2.1"',
+ "Routing entry for 10.0.2.0/24",
+ desc="Check for FIB entry for 10.0.2.1",
+ timeout=10)
+
+Notice that the call arguments can be specified by their correct position in the list or
+using keyword names, and they can also be specified over multiple lines if preferred.
+
+All of the functions are documented and defined below.
+"""
+
+# pylint: disable=global-statement
+
+import functools
+import json
+import logging
+import pprint
+import re
+import time
+
+from pathlib import Path
+from typing import Any
+from typing import Union
+
+from deepdiff import DeepDiff as json_cmp
+
+from munet.base import Commander
+
+
+class TestCaseInfo:
+ """Object to hold nestable TestCase Results."""
+
+ def __init__(self, tag: str, name: str, path: Path):
+ self.path = path.absolute()
+ self.tag = tag
+ self.name = name
+ self.steps = 0
+ self.passed = 0
+ self.failed = 0
+ self.start_time = time.time()
+ self.step_start_time = self.start_time
+ self.run_time = None
+
+ def __repr__(self):
+ return (
+ f"TestCaseInfo({self.tag} {self.name} steps {self.steps} "
+ f"p {self.passed} f {self.failed} path {self.path})"
+ )
+
+
+class TestCase:
+ """A mutest testcase.
+
+ This is normally meant to be used internally by the mutest command to
+ implement the user API. See README-mutest.org for usage details on the
+ user API.
+
+ Args:
+ tag: identity of the test in a run. (x.x...)
+ name: the name of the test case
+ path: the test file that is being executed.
+ targets: a dictionary of objects which implement ``cmd_nostatus(str)``
+ output_logger: a logger for output and other messages from the test.
+ result_logger: a logger to output the results of test steps to.
+ full_summary: if True then print entire doctstring instead of
+ only the first line in the results report
+
+ Attributes:
+ tag: identity of the test in a run
+ name: the name of the test
+ targets: dictionary of targets.
+
+ steps: total steps executed so far.
+ passed: number of passing steps.
+ failed: number of failing steps.
+
+ last: the last command output.
+ last_m: the last result of re.search during a matching step on the output with
+ newlines converted to spaces.
+
+ :meta private:
+ """
+
+ # sum_hfmt = "{:5.5s} {:4.4s} {:>6.6s} {}"
+ # sum_dfmt = "{:5s} {:4.4s} {:^6.6s} {}"
+ sum_fmt = "%-8.8s %4.4s %{}s %6s %s"
+
+ def __init__(
+ self,
+ tag: int,
+ name: str,
+ path: Path,
+ targets: dict,
+ output_logger: logging.Logger = None,
+ result_logger: logging.Logger = None,
+ full_summary: bool = False,
+ ):
+
+ self.info = TestCaseInfo(tag, name, path)
+ self.__saved_info = []
+ self.__short_doc_header = not full_summary
+
+ self.__space_before_result = False
+
+ # we are only ever in a section once, an include ends a section
+ # so are never in section+include, and another section ends a
+ # section, so we don't need __in_section to be save in the
+ # TestCaseInfo struct.
+ self.__in_section = False
+
+ self.targets = targets
+
+ self.last = ""
+ self.last_m = None
+
+ self.rlog = result_logger
+ self.olog = output_logger
+ self.logf = functools.partial(self.olog.log, logging.INFO)
+
+ oplog = logging.getLogger("mutest.oper")
+ self.oplogf = oplog.debug
+ self.oplogf("new TestCase: tag: %s name: %s path: %s", tag, name, path)
+
+ # find the longerst target name and make target field that wide
+ nmax = max(len(x) for x in targets)
+ nmax = max(nmax, len("TARGET"))
+ self.sum_fmt = TestCase.sum_fmt.format(nmax)
+
+ # Let's keep this out of summary for now
+ self.rlog.debug(self.sum_fmt, "NUMBER", "STAT", "TARGET", "TIME", "DESCRIPTION")
+ self.rlog.debug("-" * 70)
+
+ @property
+ def tag(self):
+ return self.info.tag
+
+ @property
+ def name(self):
+ return self.info.name
+
+ @property
+ def steps(self):
+ return self.info.steps
+
+ @property
+ def passed(self):
+ return self.info.passed
+
+ @property
+ def failed(self):
+ return self.info.failed
+
+ def execute(self):
+ """Execute the test case.
+
+ :meta private:
+ """
+ assert TestCase.g_tc is None
+ self.oplogf("execute")
+ try:
+ TestCase.g_tc = self
+ e = self.__exec_script(self.info.path, True, False)
+ except BaseException:
+ self.__end_test()
+ raise
+ return *self.__end_test(), e
+
+ def __del__(self):
+ if TestCase.g_tc is self:
+ logging.error("Internal error, TestCase.__end_test() was not called!")
+ TestCase.g_tc = None
+
+ def __push_execinfo(self, path: Path):
+ self.oplogf(
+ "__push_execinfo: path: %s current top is %s",
+ path,
+ pprint.pformat(self.info),
+ )
+ newname = self.name + path.stem
+ self.info.steps += 1
+ self.__saved_info.append(self.info)
+ tag = f"{self.info.tag}.{self.info.steps}"
+ self.info = TestCaseInfo(tag, newname, path)
+ self.oplogf("__push_execinfo: now on top: %s", pprint.pformat(self.info))
+
+ def __pop_execinfo(self):
+ # do something with tag?
+ finished_info = self.info
+ self.info = self.__saved_info.pop()
+ self.oplogf(" __pop_execinfo: poppped: %s", pprint.pformat(finished_info))
+ self.oplogf(" __pop_execinfo: now on top: %s", pprint.pformat(self.info))
+ return finished_info
+
+ def __print_header(self, tag, header, add_newline=False):
+ # self.olog.info(self.sum_fmt, tag, "", "", "", header)
+ self.olog.info("== %s ==", f"TEST: {tag}. {header}")
+ if add_newline:
+ self.rlog.info("")
+ self.rlog.info("%s. %s", tag, header)
+
+ def __exec_script(self, path, print_header, add_newline):
+
+ # Below was the original method to avoid the global TestCase
+ # variable; however, we need global functions so we can import them
+ # into test scripts. Without imports pylint will complain about undefined
+ # functions and the resulting christmas tree of warnings is annoying.
+ #
+ # pylint: disable=possibly-unused-variable,exec-used,redefined-outer-name
+ # include = self.include
+ # log = self.logf
+ # match_step = self.match_step
+ # match_step_json = self.match_step_json
+ # step = self.step
+ # step_json = self.step_json
+ # test = self.test
+ # wait_step = self.wait_step
+ # wait_step_json = self.wait_step_json
+
+ name = f"{path.stem}{self.tag}"
+ name = re.sub(r"\W|^(?=\d)", "_", name)
+
+ _ok_result = "marker"
+ try:
+ self.oplogf("__exec_script: path %s", path)
+ script = open(path, "r", encoding="utf-8").read()
+
+ # Load the script into a function.
+ script = script.strip()
+ s2 = (
+ # f"async def _{name}(ok_result):\n"
+ f"def _{name}(ok_result):\n"
+ + " "
+ + script.replace("\n", "\n ")
+ + "\n return ok_result\n"
+ + "\n"
+ )
+ exec(s2)
+
+ # Extract any docstring as a title.
+ if print_header:
+ title = locals()[f"_{name}"].__doc__.lstrip()
+ if self.__short_doc_header and (title := title.lstrip()):
+ if (idx := title.find("\n")) != -1:
+ title = title[:idx].strip()
+ if not title:
+ title = f"Test from file: {self.info.path.name}"
+ self.__print_header(self.info.tag, title, add_newline)
+ self.__space_before_result = False
+
+ # Execute the function.
+ result = locals()[f"_{name}"](_ok_result)
+
+ # Here's where we can do async in the future if we want.
+ # result = await locals()[f"_{name}"](_ok_result)
+ except Exception as error:
+ logging.error(
+ "Unexpected exception executing %s: %s", name, error, exc_info=True
+ )
+ return error
+ else:
+ if result is not _ok_result:
+ logging.info("%s returned early, result: %s", name, result)
+ else:
+ self.oplogf("__exec_script: name %s completed normally", name)
+ return None
+
+ def __post_result(self, target, success, rstr, logstr=None):
+ self.oplogf(
+ "__post_result: target: %s success %s rstr %s", target, success, rstr
+ )
+ if success:
+ self.info.passed += 1
+ status = "PASS"
+ outlf = self.logf
+ reslf = self.rlog.info
+ else:
+ self.info.failed += 1
+ status = "FAIL"
+ outlf = self.olog.warning
+ reslf = self.rlog.warning
+
+ self.info.steps += 1
+ if logstr is not None:
+ outlf("R:%d %s: %s" % (self.steps, status, logstr))
+
+ run_time = time.time() - self.info.step_start_time
+
+ stepstr = f"{self.tag}.{self.steps}"
+ rtimes = _delta_time_str(run_time)
+
+ if self.__space_before_result:
+ self.rlog.info("")
+ self.__space_before_result = False
+
+ reslf(self.sum_fmt, stepstr, status, target, rtimes, rstr)
+
+ # start counting for next step now
+ self.info.step_start_time = time.time()
+
+ def __end_test(self) -> (int, int):
+ """End the test log final results.
+
+ Returns:
+ number of steps, number passed, number failed, run time.
+ """
+ self.oplogf("__end_test: __in_section: %s", self.__in_section)
+ if self.__in_section:
+ self.__end_section()
+
+ passed, failed = self.info.passed, self.info.failed
+
+ # No close for loggers
+ # self.olog.close()
+ # self.rlog.close()
+ self.olog = None
+ self.rlog = None
+
+ assert (
+ TestCase.g_tc == self
+ ), "TestCase global unexpectedly someon else in __end_test"
+ TestCase.g_tc = None
+
+ self.info.run_time = time.time() - self.info.start_time
+ return passed, failed
+
+ def _command(
+ self,
+ target: str,
+ cmd: str,
+ ) -> str:
+ """Execute a ``cmd`` and return result.
+
+ Args:
+ target: the target to execute the command on.
+ cmd: string to execut on the target.
+ """
+ out = self.targets[target].cmd_nostatus(cmd, warn=False)
+ self.last = out = out.rstrip()
+ report = out if out else "<no output>"
+ self.logf("COMMAND OUTPUT:\n%s", report)
+ return out
+
+ def _command_json(
+ self,
+ target: str,
+ cmd: str,
+ ) -> dict:
+ """Execute a json ``cmd`` and return json result.
+
+ Args:
+ target: the target to execute the command on.
+ cmd: string to execut on the target.
+ """
+ out = self.targets[target].cmd_nostatus(cmd, warn=False)
+ self.last = out = out.rstrip()
+ try:
+ js = json.loads(out)
+ except Exception as error:
+ js = {}
+ self.olog.warning(
+ "JSON load failed. Check command output is in JSON format: %s",
+ error,
+ )
+ self.logf("COMMAND OUTPUT:\n%s", out)
+ return js
+
+ def _match_command(
+ self,
+ target: str,
+ cmd: str,
+ match: str,
+ expect_fail: bool,
+ flags: int,
+ ) -> (bool, Union[str, list]):
+ """Execute a ``cmd`` and check result.
+
+ Args:
+ target: the target to execute the command on.
+ cmd: string to execute on the target.
+ match: regex to ``re.search()`` for in output.
+ expect_fail: if True then succeed when the regexp doesn't match.
+ flags: python regex flags to modify matching behavior
+
+ Returns:
+ (success, matches): if the match fails then "matches" will be None,
+ otherwise if there were matching groups then groups() will be returned in
+ ``matches`` otherwise group(0) (i.e., the matching text).
+ """
+ out = self._command(target, cmd)
+ search = re.search(match, out, flags)
+ self.last_m = search
+ if search is None:
+ success = expect_fail
+ ret = None
+ else:
+ success = not expect_fail
+ ret = search.groups()
+ if not ret:
+ ret = search.group(0)
+
+ level = logging.DEBUG if success else logging.WARNING
+ self.olog.log(level, "matched:%s:", ret)
+ return success, ret
+
+ def _match_command_json(
+ self,
+ target: str,
+ cmd: str,
+ match: Union[str, dict],
+ expect_fail: bool,
+ ) -> Union[str, dict]:
+ """Execute a json ``cmd`` and check result.
+
+ Args:
+ target: the target to execute the command on.
+ cmd: string to execut on the target.
+ match: A json ``str`` or object (``dict``) to compare against the json
+ output from ``cmd``.
+ expect_fail: if True then succeed when the json doesn't match.
+ """
+ js = self._command_json(target, cmd)
+ try:
+ expect = json.loads(match)
+ except Exception as error:
+ expect = {}
+ self.olog.warning(
+ "JSON load failed. Check match value is in JSON format: %s", error
+ )
+
+ if json_diff := json_cmp(expect, js):
+ success = expect_fail
+ if not success:
+ self.logf("JSON DIFF:%s:" % json_diff)
+ return success, json_diff
+
+ success = not expect_fail
+ return success, js
+
+ def _wait(
+ self,
+ target: str,
+ cmd: str,
+ match: Union[str, dict],
+ is_json: bool,
+ timeout: float,
+ interval: float,
+ expect_fail: bool,
+ flags: int,
+ ) -> Union[str, dict]:
+ """Execute a command repeatedly waiting for result until timeout."""
+ startt = time.time()
+ endt = startt + timeout
+
+ success = False
+ ret = None
+ while not success and time.time() < endt:
+ if is_json:
+ success, ret = self._match_command_json(target, cmd, match, expect_fail)
+ else:
+ success, ret = self._match_command(
+ target, cmd, match, expect_fail, flags
+ )
+ if not success:
+ time.sleep(interval)
+ return success, ret
+
+ # ---------------------
+ # Public APIs for User
+ # ---------------------
+
+ def include(self, pathname: str, new_section: bool = False):
+ """See :py:func:`~munet.mutest.userapi.include`.
+
+ :meta private:
+ """
+ path = Path(pathname)
+ path = self.info.path.parent.joinpath(path)
+
+ self.oplogf(
+ "include: new path: %s create section: %s currently __in_section: %s",
+ path,
+ new_section,
+ self.__in_section,
+ )
+
+ if new_section:
+ self.oplogf("include: starting new exec section")
+ self.__start_exec_section(path)
+ our_info = self.info
+ # Note we do *not* mark __in_section True
+ else:
+ # swap the current path inside the top info
+ old_path = self.info.path
+ self.info.path = path
+ self.oplogf("include: swapped info path: new %s old %s", path, old_path)
+
+ self.__exec_script(path, print_header=new_section, add_newline=new_section)
+
+ if new_section:
+ # Something within the section creating include has also created a section
+ # end it, sections do not cross section creating file boundaries
+ if self.__in_section:
+ self.oplogf(
+ "include done: path: %s __in_section calling __end_section", path
+ )
+ self.__end_section()
+
+ # We should now be back to the info we started with, b/c we don't actually
+ # start a new section (__in_section) that then could have been ended inside
+ # the included file.
+ assert our_info == self.info
+
+ self.oplogf(
+ "include done: path: %s new_section calling __end_section", path
+ )
+ self.__end_section()
+ else:
+ # The current top path could be anything due to multiple inline includes as
+ # well as section swap in and out. Forcibly return the top path to the file
+ # we are returning to
+ self.info.path = old_path
+ self.oplogf("include: restored info path: %s", old_path)
+
+ def __end_section(self):
+ self.oplogf("__end_section: __in_section: %s", self.__in_section)
+ info = self.__pop_execinfo()
+ passed, failed = info.passed, info.failed
+ self.info.passed += passed
+ self.info.failed += failed
+ self.__space_before_result = True
+ self.oplogf("__end_section setting __in_section to False")
+ self.__in_section = False
+
+ def __start_exec_section(self, path):
+ self.oplogf("__start_exec_section: __in_section: %s", self.__in_section)
+ if self.__in_section:
+ self.__end_section()
+
+ self.__push_execinfo(path)
+ self.__space_before_result = False
+ self.oplogf("NOT setting __in_section to True")
+ assert not self.__in_section
+
+ def section(self, desc: str):
+ """See :py:func:`~munet.mutest.userapi.section`.
+
+ :meta private:
+ """
+ self.oplogf("section: __in_section: %s", self.__in_section)
+ # Grab path before we pop the current info off the top
+ path = self.info.path
+ old_steps = self.info.steps
+
+ if self.__in_section:
+ self.__end_section()
+
+ self.__push_execinfo(path)
+ add_nl = self.info.steps <= old_steps
+
+ self.__space_before_result = False
+ self.__in_section = True
+ self.oplogf(" section setting __in_section to True")
+ self.__print_header(self.info.tag, desc, add_nl)
+
+ def step(self, target: str, cmd: str) -> str:
+ """See :py:func:`~munet.mutest.userapi.step`.
+
+ :meta private:
+ """
+ self.logf(
+ "#%s.%s:%s:STEP:%s:%s",
+ self.tag,
+ self.steps + 1,
+ self.info.path,
+ target,
+ cmd,
+ )
+ return self._command(target, cmd)
+
+ def step_json(self, target: str, cmd: str) -> dict:
+ """See :py:func:`~munet.mutest.userapi.step_json`.
+
+ :meta private:
+ """
+ self.logf(
+ "#%s.%s:%s:STEP_JSON:%s:%s",
+ self.tag,
+ self.steps + 1,
+ self.info.path,
+ target,
+ cmd,
+ )
+ return self._command_json(target, cmd)
+
+ def match_step(
+ self,
+ target: str,
+ cmd: str,
+ match: str,
+ desc: str = "",
+ expect_fail: bool = False,
+ flags: int = re.DOTALL,
+ ) -> (bool, Union[str, list]):
+ """See :py:func:`~munet.mutest.userapi.match_step`.
+
+ :meta private:
+ """
+ self.logf(
+ "#%s.%s:%s:MATCH_STEP:%s:%s:%s:%s:%s:%s",
+ self.tag,
+ self.steps + 1,
+ self.info.path,
+ target,
+ cmd,
+ match,
+ desc,
+ expect_fail,
+ flags,
+ )
+ success, ret = self._match_command(target, cmd, match, expect_fail, flags)
+ if desc:
+ self.__post_result(target, success, desc)
+ return success, ret
+
+ def test_step(self, expr_or_value: Any, desc: str, target: str = "") -> bool:
+ """See :py:func:`~munet.mutest.userapi.test`.
+
+ :meta private:
+ """
+ success = bool(expr_or_value)
+ self.__post_result(target, success, desc)
+ return success
+
+ def match_step_json(
+ self,
+ target: str,
+ cmd: str,
+ match: Union[str, dict],
+ desc: str = "",
+ expect_fail: bool = False,
+ ) -> (bool, Union[str, dict]):
+ """See :py:func:`~munet.mutest.userapi.match_step_json`.
+
+ :meta private:
+ """
+ self.logf(
+ "#%s.%s:%s:MATCH_STEP_JSON:%s:%s:%s:%s:%s",
+ self.tag,
+ self.steps + 1,
+ self.info.path,
+ target,
+ cmd,
+ match,
+ desc,
+ expect_fail,
+ )
+ success, ret = self._match_command_json(target, cmd, match, expect_fail)
+ if desc:
+ self.__post_result(target, success, desc)
+ return success, ret
+
+ def wait_step(
+ self,
+ target: str,
+ cmd: str,
+ match: Union[str, dict],
+ desc: str = "",
+ timeout=10,
+ interval=0.5,
+ expect_fail: bool = False,
+ flags: int = re.DOTALL,
+ ) -> (bool, Union[str, list]):
+ """See :py:func:`~munet.mutest.userapi.wait_step`.
+
+ :meta private:
+ """
+ if interval is None:
+ interval = min(timeout / 20, 0.25)
+ self.logf(
+ "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s:%s",
+ self.tag,
+ self.steps + 1,
+ self.info.path,
+ target,
+ cmd,
+ match,
+ timeout,
+ interval,
+ desc,
+ expect_fail,
+ flags,
+ )
+ success, ret = self._wait(
+ target, cmd, match, False, timeout, interval, expect_fail, flags
+ )
+ if desc:
+ self.__post_result(target, success, desc)
+ return success, ret
+
+ def wait_step_json(
+ self,
+ target: str,
+ cmd: str,
+ match: Union[str, dict],
+ desc: str = "",
+ timeout=10,
+ interval=None,
+ expect_fail: bool = False,
+ ) -> (bool, Union[str, dict]):
+ """See :py:func:`~munet.mutest.userapi.wait_step_json`.
+
+ :meta private:
+ """
+ if interval is None:
+ interval = min(timeout / 20, 0.25)
+ self.logf(
+ "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s",
+ self.tag,
+ self.steps + 1,
+ self.info.path,
+ target,
+ cmd,
+ match,
+ timeout,
+ interval,
+ desc,
+ expect_fail,
+ )
+ success, ret = self._wait(
+ target, cmd, match, True, timeout, interval, expect_fail, 0
+ )
+ if desc:
+ self.__post_result(target, success, desc)
+ return success, ret
+
+
+# A non-rentrant global to allow for simplified operations
+TestCase.g_tc = None
+
+# pylint: disable=protected-access
+
+
+def _delta_time_str(run_time: float) -> str:
+ if run_time < 0.0001:
+ return "0.0"
+ if run_time < 0.001:
+ return f"{run_time:1.4f}"
+ if run_time < 0.01:
+ return f"{run_time:2.3f}"
+ if run_time < 0.1:
+ return f"{run_time:3.2f}"
+ if run_time < 100:
+ return f"{run_time:4.1f}"
+ return f"{run_time:5f}s"
+
+
+def section(desc: str):
+ """Start a new section for steps, with a description.
+
+ This starts a new section of tests. The result is basically
+ the same as doing a non-inline include. The current test number
+ is used to form a new sub-set of test steps. So if the current
+ test number is 2.3, a section will now number subsequent steps
+ 2.3.1, 2.3.2, ...
+
+ A subsequent :py:func:`section` or non-inline :py:func:`include`
+ call ends the current section and advances the base test number.
+
+ Args:
+ desc: the description for the new section.
+ """
+ TestCase.g_tc.section(desc)
+
+
+def log(fmt, *args, **kwargs):
+ """Log a message in the testcase output log."""
+ return TestCase.g_tc.logf(fmt, *args, **kwargs)
+
+
+def include(pathname: str, new_section=False):
+ """Include a file as part of testcase.
+
+ Args:
+ pathname: the file to include.
+ new_section: if a new section should be created, otherwise
+ commands are executed inline.
+ """
+ return TestCase.g_tc.include(pathname, new_section)
+
+
+def script_dir() -> Path:
+ """The pathname to the directory containing the current script file.
+
+ When an include() is called the script_dir is updated to be current with the
+ includeded file, and is reverted to the previous value when the include completes.
+ """
+ return TestCase.g_tc.info.path.parent
+
+
+def get_target(name: str) -> Commander:
+ """Get the target object with the given ``name``."""
+ return TestCase.g_tc.targets[name]
+
+
+def step(target: str, cmd: str) -> str:
+ """Execute a ``cmd`` on a ``target`` and return the output.
+
+ Args:
+ target: the target to execute the ``cmd`` on.
+ cmd: string to execute on the target.
+
+ Returns:
+ Returns the ``str`` output of the ``cmd``.
+ """
+ return TestCase.g_tc.step(target, cmd)
+
+
+def step_json(target: str, cmd: str) -> dict:
+ """Execute a json ``cmd`` on a ``target`` and return the json object.
+
+ Args:
+ target: the target to execute the ``cmd`` on.
+ cmd: string to execute on the target.
+
+ Returns:
+ Returns the json object after parsing the ``cmd`` output.
+
+ If json parse fails, a warning is logged and an empty ``dict`` is used.
+ """
+ return TestCase.g_tc.step_json(target, cmd)
+
+
+def test_step(expr_or_value: Any, desc: str, target: str = "") -> bool:
+ """Evaluates ``expr_or_value`` and posts a result base on it bool(expr).
+
+ If ``expr_or_value`` evaluates to a positive result (i.e., True, non-zero, non-None,
+ non-empty string, non-empty list, etc..) then a PASS result is recorded, otherwise
+ record a FAIL is recorded.
+
+ Args:
+ expr: an expression or value to evaluate
+ desc: description of this test step.
+ target: optional target to associate with this test in the result string.
+
+ Returns:
+ A bool indicating the test PASS or FAIL result.
+ """
+ return TestCase.g_tc.test_step(expr_or_value, desc, target)
+
+
+def match_step(
+ target: str,
+ cmd: str,
+ match: str,
+ desc: str = "",
+ expect_fail: bool = False,
+ flags: int = re.DOTALL,
+) -> (bool, Union[str, list]):
+ """Execute a ``cmd`` on a ``target`` check result.
+
+ Execute ``cmd`` on ``target`` and check if the regexp in ``match``
+ matches or doesn't match (according to the ``expect_fail`` value) the
+ ``cmd`` output.
+
+ If the ``match`` regexp includes groups and if the match succeeds
+ the group values will be returned in a list, otherwise the command
+ output is returned.
+
+ Args:
+ target: the target to execute the ``cmd`` on.
+ cmd: string to execut on the ``target``.
+ match: regex to match against output.
+ desc: description of test, if no description then no result is logged.
+ expect_fail: if True then succeed when the regexp doesn't match.
+ flags: python regex flags to modify matching behavior
+
+ Returns:
+ Returns a 2-tuple. The first value is a bool indicating ``success``.
+ The second value will be a list from ``re.Match.groups()`` if non-empty,
+ otherwise ``re.Match.group(0)`` if there was a match otherwise None.
+ """
+ return TestCase.g_tc.match_step(target, cmd, match, desc, expect_fail, flags)
+
+
+def match_step_json(
+ target: str,
+ cmd: str,
+ match: Union[str, dict],
+ desc: str = "",
+ expect_fail: bool = False,
+) -> (bool, Union[str, dict]):
+ """Execute a ``cmd`` on a ``target`` check result.
+
+ Execute ``cmd`` on ``target`` and check if the json object in ``match``
+ matches or doesn't match (according to the ``expect_fail`` value) the
+ json output from ``cmd``.
+
+ Args:
+ target: the target to execute the ``cmd`` on.
+ cmd: string to execut on the ``target``.
+ match: A json ``str`` or object (``dict``) to compare against the json
+ output from ``cmd``.
+ desc: description of test, if no description then no result is logged.
+ expect_fail: if True then succeed if the a json doesn't match.
+
+ Returns:
+ Returns a 2-tuple. The first value is a bool indicating ``success``. The
+ second value is a ``str`` diff if there is a difference found in the json
+ compare, otherwise the value is the json object (``dict``) from the ``cmd``.
+
+ If json parse fails, a warning is logged and an empty ``dict`` is used.
+ """
+ return TestCase.g_tc.match_step_json(target, cmd, match, desc, expect_fail)
+
+
+def wait_step(
+ target: str,
+ cmd: str,
+ match: Union[str, dict],
+ desc: str = "",
+ timeout: float = 10.0,
+ interval: float = 0.5,
+ expect_fail: bool = False,
+ flags: int = re.DOTALL,
+) -> (bool, Union[str, list]):
+ """Execute a ``cmd`` on a ``target`` repeatedly, looking for a result.
+
+ Execute ``cmd`` on ``target``, every ``interval`` seconds for up to ``timeout``
+ seconds until the output of ``cmd`` does or doesn't match (according to the
+ ``expect_fail`` value) the ``match`` value.
+
+ Args:
+ target: the target to execute the ``cmd`` on.
+ cmd: string to execut on the ``target``.
+ match: regexp to match against output.
+ timeout: The number of seconds to repeat the ``cmd`` looking for a match
+ (or non-match if ``expect_fail`` is True).
+ interval: The number of seconds between running the ``cmd``. If not
+ specified the value is calculated from the timeout value so that on
+ average the cmd will execute 10 times. The minimum calculated interval
+ is .25s, shorter values can be passed explicitly.
+ desc: description of test, if no description then no result is logged.
+ expect_fail: if True then succeed when the regexp *doesn't* match.
+ flags: python regex flags to modify matching behavior
+
+ Returns:
+ Returns a 2-tuple. The first value is a bool indicating ``success``.
+ The second value will be a list from ``re.Match.groups()`` if non-empty,
+ otherwise ``re.Match.group(0)`` if there was a match otherwise None.
+ """
+ return TestCase.g_tc.wait_step(
+ target, cmd, match, desc, timeout, interval, expect_fail, flags
+ )
+
+
+def wait_step_json(
+ target: str,
+ cmd: str,
+ match: Union[str, dict],
+ desc: str = "",
+ timeout=10,
+ interval=None,
+ expect_fail: bool = False,
+) -> (bool, Union[str, dict]):
+ """Execute a cmd repeatedly and wait for matching result.
+
+ Execute ``cmd`` on ``target``, every ``interval`` seconds until
+ the output of ``cmd`` matches or doesn't match (according to the
+ ``expect_fail`` value) ``match``, for up to ``timeout`` seconds.
+
+ ``match`` is a regular expression to search for in the output of ``cmd`` when
+ ``is_json`` is False.
+
+ When ``is_json`` is True ``match`` must be a json object or a ``str`` which
+ parses into a json object. Likewise, the ``cmd`` output is parsed into a json
+ object and then a comparison is done between the two json objects.
+
+ Args:
+ target: the target to execute the ``cmd`` on.
+ cmd: string to execut on the ``target``.
+ match: A json object or str representation of one to compare against json
+ output from ``cmd``.
+ desc: description of test, if no description then no result is logged.
+ timeout: The number of seconds to repeat the ``cmd`` looking for a match
+ (or non-match if ``expect_fail`` is True).
+ interval: The number of seconds between running the ``cmd``. If not
+ specified the value is calculated from the timeout value so that on
+ average the cmd will execute 10 times. The minimum calculated interval
+ is .25s, shorter values can be passed explicitly.
+ expect_fail: if True then succeed if the a json doesn't match.
+
+ Returns:
+ Returns a 2-tuple. The first value is a bool indicating ``success``.
+ The second value is a ``str`` diff if there is a difference found in the
+ json compare, otherwise the value is a json object (dict) from the ``cmd``
+ output.
+
+ If json parse fails, a warning is logged and an empty ``dict`` is used.
+ """
+ return TestCase.g_tc.wait_step_json(
+ target, cmd, match, desc, timeout, interval, expect_fail
+ )
+
+
+def luInclude(filename, CallOnFail=None):
+ """Backward compatible API, do not use in new tests."""
+ return include(filename)
+
+
+def luLast(usenl=False):
+ """Backward compatible API, do not use in new tests."""
+ del usenl
+ return TestCase.g_tc.last_m
+
+
+def luCommand(
+ target,
+ cmd,
+ regexp=".",
+ op="none",
+ result="",
+ ltime=10,
+ returnJson=False,
+ wait_time=0.5,
+):
+ """Backward compatible API, do not use in new tests.
+
+ Only non-json is verified to any degree of confidence by code inspection.
+
+ For non-json should return match.group() if match else return bool(op == "fail").
+
+ For json if no diff return the json else diff return bool(op == "jsoncmp_fail")
+ bug if no json from output (fail parse) could maybe generate diff, which could
+ then return
+ """
+ if op == "wait":
+ if returnJson:
+ return wait_step_json(target, cmd, regexp, result, ltime, wait_time)
+
+ success, _ = wait_step(target, cmd, regexp, result, ltime, wait_time)
+ match = luLast()
+ if success and match is not None:
+ return match.group()
+ return success
+
+ if op == "none":
+ if returnJson:
+ return step_json(target, cmd)
+ return step(target, cmd)
+
+ if returnJson and op in ("jsoncmp_fail", "jsoncmp_pass"):
+ expect_fail = op == "jsoncmp_fail"
+ return match_step_json(target, cmd, regexp, result, expect_fail)
+
+ assert not returnJson
+ assert op in ("fail", "pass")
+ expect_fail = op == "fail"
+ success, _ = match_step(target, cmd, regexp, result, expect_fail)
+ match = luLast()
+ if success and match is not None:
+ return match.group()
+ return success
diff --git a/tests/topotests/munet/mutestshare.py b/tests/topotests/munet/mutestshare.py
new file mode 100644
index 0000000..95ffa74
--- /dev/null
+++ b/tests/topotests/munet/mutestshare.py
@@ -0,0 +1,254 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# January 28 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+"""A tiny init for namespaces in python inspired by the C program tini."""
+import argparse
+import errno
+import logging
+import os
+import shlex
+import signal
+import subprocess
+import sys
+import threading
+import time
+
+from signal import Signals as S
+
+from . import linux
+from .base import commander
+
+
+child_pid = -1
+very_verbose = False
+restore_signals = set()
+
+
+def vdebug(*args, **kwargs):
+ if very_verbose:
+ logging.debug(*args, **kwargs)
+
+
+def exit_with_status(pid, status):
+ try:
+ ec = status >> 8 if bool(status & 0xFF00) else status | 0x80
+ logging.debug("reaped our child, exiting %s", ec)
+ sys.exit(ec)
+ except ValueError:
+ vdebug("pid %s didn't actually exit", pid)
+
+
+def waitpid(tag):
+ logging.debug("%s: waitid for exiting processes", tag)
+ idobj = os.waitid(os.P_ALL, 0, os.WEXITED)
+ pid = idobj.si_pid
+ status = idobj.si_status
+ if pid == child_pid:
+ exit_with_status(pid, status)
+ else:
+ logging.debug("%s: reaped zombie pid %s with status %s", tag, pid, status)
+
+
+def new_process_group():
+ pid = os.getpid()
+ try:
+ pgid = os.getpgrp()
+ if pgid == pid:
+ logging.debug("already process group leader %s", pgid)
+ else:
+ logging.debug("creating new process group %s", pid)
+ os.setpgid(pid, 0)
+ except Exception as error:
+ logging.warning("unable to get new process group: %s", error)
+ return
+
+ # Block these in order to allow foregrounding, otherwise we'd get SIGTTOU blocked
+ signal.signal(S.SIGTTIN, signal.SIG_IGN)
+ signal.signal(S.SIGTTOU, signal.SIG_IGN)
+ fd = sys.stdin.fileno()
+ if not os.isatty(fd):
+ logging.debug("stdin not a tty no foregrounding required")
+ else:
+ try:
+ # This will error if our session no longer associated with controlling tty.
+ pgid = os.tcgetpgrp(fd)
+ if pgid == pid:
+ logging.debug("process group already in foreground %s", pgid)
+ else:
+ logging.debug("making us the foreground pgid backgrounding %s", pgid)
+ os.tcsetpgrp(fd, pid)
+ except OSError as error:
+ if error.errno == errno.ENOTTY:
+ logging.debug("session is no longer associated with controlling tty")
+ else:
+ logging.warning("unable to foreground pgid %s: %s", pid, error)
+ signal.signal(S.SIGTTIN, signal.SIG_DFL)
+ signal.signal(S.SIGTTOU, signal.SIG_DFL)
+
+
+def exec_child(exec_args):
+ # Restore signals to default handling:
+ for snum in restore_signals:
+ signal.signal(snum, signal.SIG_DFL)
+
+ # Create new process group.
+ new_process_group()
+
+ estring = shlex.join(exec_args)
+ try:
+ # and exec the process
+ logging.debug("child: executing '%s'", estring)
+ os.execvp(exec_args[0], exec_args)
+ # NOTREACHED
+ except Exception as error:
+ logging.warning("child: unable to execute '%s': %s", estring, error)
+ raise
+
+
+def is_creating_pid_namespace():
+ p1name = subprocess.check_output(
+ "readlink /proc/self/pid", stderr=subprocess.STDOUT, shell=True
+ )
+ p2name = subprocess.check_output(
+ "readlink /proc/self/pid_for_children", stderr=subprocess.STDOUT, shell=True
+ )
+ return p1name != p2name
+
+
+def restore_namespace(ppid_fd, uflags):
+ fd = ppid_fd
+ retry = 3
+ for i in range(0, retry):
+ try:
+ linux.setns(fd, uflags)
+ except OSError as error:
+ logging.warning("could not reset to old namespace fd %s: %s", fd, error)
+ if i == retry - 1:
+ raise
+ time.sleep(1)
+ os.close(fd)
+
+
+def create_thread_test():
+ def runthread(name):
+ logging.info("In thread: %s", name)
+
+ logging.info("Create thread")
+ thread = threading.Thread(target=runthread, args=(1,))
+ logging.info("Run thread")
+ thread.start()
+ logging.info("Join thread")
+ thread.join()
+
+
+def run(args):
+ del args
+ # We look for this b/c the unshare pid will share with /sibn/init
+ # nselm = "pid_for_children"
+ # nsflags.append(f"--pid={pp / nselm}")
+ # mutini now forks when created this way
+ # cmd.append("--pid")
+ # cmd.append("--fork")
+ # cmd.append("--kill-child")
+ # cmd.append("--mount-proc")
+
+ uflags = linux.CLONE_NEWPID
+ nslist = ["pid_for_children"]
+ uflags |= linux.CLONE_NEWNS
+ nslist.append("mnt")
+ uflags |= linux.CLONE_NEWNET
+ nslist.append("net")
+
+ # Before values
+ pid = os.getpid()
+ nsdict = {x: os.readlink(f"/tmp/mu-global-proc/{pid}/ns/{x}") for x in nslist}
+
+ #
+ # UNSHARE
+ #
+ create_thread_test()
+
+ ppid = os.getppid()
+ ppid_fd = linux.pidfd_open(ppid)
+ linux.unshare(uflags)
+
+ # random syscall's fail until we fork a child to establish the new pid namespace.
+ global child_pid # pylint: disable=global-statement
+ child_pid = os.fork()
+ if not child_pid:
+ logging.info("In child sleeping")
+ time.sleep(1200)
+ sys.exit(1)
+
+ # verify after values differ
+ nnsdict = {x: os.readlink(f"/tmp/mu-global-proc/{pid}/ns/{x}") for x in nslist}
+ assert not {k for k in nsdict if nsdict[k] == nnsdict[k]}
+
+ # Remount / and any future mounts below it as private
+ commander.cmd_raises("mount --make-rprivate /")
+ # Mount a new /proc in our new namespace
+ commander.cmd_raises("mount -t proc proc /proc")
+
+ #
+ # In NEW NS
+ #
+
+ cid = os.fork()
+ if not cid:
+ logging.info("In second child sleeping")
+ time.sleep(4)
+ sys.exit(1)
+ logging.info("Waiting for second child")
+ os.waitpid(cid, 0)
+
+ try:
+ create_thread_test()
+ except Exception as error:
+ print(error)
+
+ #
+ # RESTORE
+ #
+
+ logging.info("In new namespace, restoring old")
+ # Make sure we can go back, not sure since this is PID namespace, but maybe
+ restore_namespace(ppid_fd, uflags)
+
+ # verify after values the same
+ nnsdict = {x: os.readlink(f"/proc/self/ns/{x}") for x in nslist}
+ assert nsdict == nnsdict
+
+
+def main():
+ ap = argparse.ArgumentParser()
+ ap.add_argument(
+ "-v", dest="verbose", action="count", default=0, help="More -v's, more verbose"
+ )
+ ap.add_argument("rest", nargs=argparse.REMAINDER)
+ args = ap.parse_args()
+
+ level = logging.DEBUG if args.verbose else logging.INFO
+ if args.verbose > 1:
+ global very_verbose # pylint: disable=global-statement
+ very_verbose = True
+ logging.basicConfig(
+ level=level, format="%(asctime)s mutini: %(levelname)s: %(message)s"
+ )
+
+ status = 4
+ try:
+ run(args)
+ except KeyboardInterrupt:
+ logging.info("exiting (main), received KeyboardInterrupt in main")
+ except Exception as error:
+ logging.info("exiting (main), unexpected exception %s", error, exc_info=True)
+
+ sys.exit(status)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/topotests/munet/mutini.py b/tests/topotests/munet/mutini.py
new file mode 100755
index 0000000..e5f9931
--- /dev/null
+++ b/tests/topotests/munet/mutini.py
@@ -0,0 +1,432 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# January 28 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+"""A tiny init for namespaces in python inspired by the C program tini."""
+
+
+# pylint: disable=global-statement
+import argparse
+import errno
+import logging
+import os
+import re
+import shlex
+import signal
+import subprocess
+import sys
+
+from signal import Signals as S
+
+
+try:
+ from munet import linux
+except ModuleNotFoundError:
+ # We cannot use relative imports and still run this module directly as a script, and
+ # there are some use cases where we want to run this file as a script.
+ sys.path.append(os.path.dirname(os.path.realpath(__file__)))
+ import linux
+
+
+class g:
+ """Global variables for our program."""
+
+ child_pid = -1
+ orig_pid = os.getpid()
+ exit_signal = False
+ pid_status_cache = {}
+ restore_signals = set()
+ very_verbose = False
+
+
+unshare_flags = {
+ "C": linux.CLONE_NEWCGROUP,
+ "i": linux.CLONE_NEWIPC,
+ "m": linux.CLONE_NEWNS,
+ "n": linux.CLONE_NEWNET,
+ "p": linux.CLONE_NEWPID,
+ "u": linux.CLONE_NEWUTS,
+ "T": linux.CLONE_NEWTIME,
+}
+
+
+ignored_signals = {
+ S.SIGTTIN,
+ S.SIGTTOU,
+}
+abort_signals = {
+ S.SIGABRT,
+ S.SIGBUS,
+ S.SIGFPE,
+ S.SIGILL,
+ S.SIGKILL,
+ S.SIGSEGV,
+ S.SIGSTOP,
+ S.SIGSYS,
+ S.SIGTRAP,
+}
+no_prop_signals = abort_signals | ignored_signals | {S.SIGCHLD}
+
+
+def vdebug(*args, **kwargs):
+ if g.very_verbose:
+ logging.debug(*args, **kwargs)
+
+
+def get_pid_status_item(status, stat):
+ m = re.search(rf"(?:^|\n){stat}:\t(.*)(?:\n|$)", status)
+ return m.group(1).strip() if m else None
+
+
+def pget_pid_status_item(pid, stat):
+ if pid not in g.pid_status_cache:
+ with open(f"/proc/{pid}/status", "r", encoding="utf-8") as f:
+ g.pid_status_cache[pid] = f.read().strip()
+ return get_pid_status_item(g.pid_status_cache[pid], stat).strip()
+
+
+def get_pid_name(pid):
+ try:
+ return get_pid_status_item(g.pid_status_cache[pid], "Name")
+ except Exception:
+ return str(pid)
+
+
+# def init_get_child_pids():
+# """Return list of "children" pids.
+# We consider any process with a 0 parent pid to also be our child as it
+# nsentered our pid namespace from an external parent.
+# """
+# g.pid_status_cache.clear()
+# pids = (int(x) for x in os.listdir("/proc") if x.isdigit() and x != "1")
+# return (
+# x for x in pids if x == g.child_pid or pget_pid_status_item(x, "PPid") == "0"
+# )
+
+
+def exit_with_status(status):
+ if os.WIFEXITED(status):
+ ec = os.WEXITSTATUS(status)
+ elif os.WIFSIGNALED(status):
+ ec = 0x80 | os.WTERMSIG(status)
+ else:
+ ec = 255
+ logging.debug("exiting with code %s", ec)
+ sys.exit(ec)
+
+
+def waitpid(tag):
+ logging.debug("%s: waitid for exiting process", tag)
+ idobj = os.waitid(os.P_ALL, 0, os.WEXITED)
+ pid = idobj.si_pid
+ status = idobj.si_status
+
+ if pid != g.child_pid:
+ pidname = get_pid_name(pid)
+ logging.debug(
+ "%s: reaped zombie %s (%s) w/ status %s", tag, pid, pidname, status
+ )
+ return
+
+ logging.debug("reaped child with status %s", status)
+ exit_with_status(status)
+ # NOTREACHED
+
+
+def sig_trasmit(signum, _):
+ signame = signal.Signals(signum).name
+ if g.child_pid == -1:
+ # We've received a signal after setting up to be init proc
+ # but prior to fork or fork returning with child pid
+ logging.debug("received %s prior to child exec, exiting", signame)
+ sys.exit(0x80 | signum)
+
+ try:
+ os.kill(g.child_pid, signum)
+ except OSError as error:
+ if error.errno != errno.ESRCH:
+ logging.error(
+ "error forwarding signal %s to child, exiting: %s", signum, error
+ )
+ sys.exit(0x80 | signum)
+ logging.debug("child pid %s exited prior to signaling", g.child_pid)
+
+
+def sig_sigchld(signum, _):
+ assert signum == S.SIGCHLD
+ try:
+ waitpid("SIGCHLD")
+ except ChildProcessError as error:
+ logging.warning("got SIGCHLD but no pid to wait on: %s", error)
+
+
+def setup_init_signals():
+ valid = set(signal.valid_signals())
+ named = set(x.value for x in signal.Signals)
+ for snum in sorted(named):
+ if snum not in valid:
+ continue
+ if S.SIGRTMIN <= snum <= S.SIGRTMAX:
+ continue
+
+ sname = signal.Signals(snum).name
+ if snum == S.SIGCHLD:
+ vdebug("installing local handler for %s", sname)
+ signal.signal(snum, sig_sigchld)
+ g.restore_signals.add(snum)
+ elif snum in ignored_signals:
+ vdebug("installing ignore handler for %s", sname)
+ signal.signal(snum, signal.SIG_IGN)
+ g.restore_signals.add(snum)
+ elif snum in abort_signals:
+ vdebug("leaving default handler for %s", sname)
+ # signal.signal(snum, signal.SIG_DFL)
+ else:
+ vdebug("installing trasmit signal handler for %s", sname)
+ try:
+ signal.signal(snum, sig_trasmit)
+ g.restore_signals.add(snum)
+ except OSError as error:
+ logging.warning(
+ "failed installing signal handler for %s: %s", sname, error
+ )
+
+
+def new_process_group():
+ """Create and lead a new process group.
+
+ This function will create a new process group if we are not yet leading one, and
+ additionally foreground said process group in our session. This foregrounding
+ action is copied from tini, and I believe serves a purpose when serving as init
+ for a container (e.g., podman).
+ """
+ pid = os.getpid()
+ try:
+ pgid = os.getpgrp()
+ if pgid == pid:
+ logging.debug("already process group leader %s", pgid)
+ else:
+ logging.debug("creating new process group %s", pid)
+ os.setpgid(pid, 0)
+ except Exception as error:
+ logging.warning("unable to get new process group: %s", error)
+ return
+
+ # Block these in order to allow foregrounding, otherwise we'd get SIGTTOU blocked
+ signal.signal(S.SIGTTIN, signal.SIG_IGN)
+ signal.signal(S.SIGTTOU, signal.SIG_IGN)
+ fd = sys.stdin.fileno()
+ if not os.isatty(fd):
+ logging.debug("stdin not a tty no foregrounding required")
+ else:
+ try:
+ # This will error if our session no longer associated with controlling tty.
+ pgid = os.tcgetpgrp(fd)
+ if pgid == pid:
+ logging.debug("process group already in foreground %s", pgid)
+ else:
+ logging.debug("making us the foreground pgid backgrounding %s", pgid)
+ os.tcsetpgrp(fd, pid)
+ except OSError as error:
+ if error.errno == errno.ENOTTY:
+ logging.debug("session is no longer associated with controlling tty")
+ else:
+ logging.warning("unable to foreground pgid %s: %s", pid, error)
+ signal.signal(S.SIGTTIN, signal.SIG_DFL)
+ signal.signal(S.SIGTTOU, signal.SIG_DFL)
+
+
+def is_creating_pid_namespace():
+ p1name = subprocess.check_output(
+ "readlink /proc/self/pid", stderr=subprocess.STDOUT, shell=True
+ )
+ p2name = subprocess.check_output(
+ "readlink /proc/self/pid_for_children", stderr=subprocess.STDOUT, shell=True
+ )
+ return p1name != p2name
+
+
+def be_init(new_pg, exec_args):
+ #
+ # Arrange for us to be killed when our parent dies, this will subsequently also kill
+ # all procs in any PID namespace we are init for.
+ #
+ logging.debug("set us to be SIGKILLed when parent exits")
+ linux.set_parent_death_signal(signal.SIGKILL)
+
+ # If we are createing a new PID namespace for children...
+ if g.orig_pid != 1:
+ logging.debug("started as pid %s", g.orig_pid)
+ # assert is_creating_pid_namespace()
+
+ # Fork to become pid 1
+ logging.debug("forking to become pid 1")
+ child_pid = os.fork()
+ if child_pid:
+ logging.debug("in parent waiting on child pid %s to exit", child_pid)
+ status = os.wait()
+ logging.debug("got child exit status %s", status)
+ exit_with_status(status)
+ # NOTREACHED
+
+ # We must be pid 1 now.
+ logging.debug("in child as pid %s", os.getpid())
+ assert os.getpid() == 1
+
+ # We need a new /proc now.
+ logging.debug("mount new /proc")
+ linux.mount("proc", "/proc", "proc")
+
+ # If the parent exists kill us using SIGKILL
+ logging.debug("set us to be SIGKILLed when parent exits")
+ linux.set_parent_death_signal(signal.SIGKILL)
+
+ if not exec_args:
+ if not new_pg:
+ logging.debug("no exec args, no new process group")
+ # # if 0 == os.getpgid(0):
+ # status = os.setpgid(0, 1)
+ # logging.debug("os.setpgid(0, 1) == %s", status)
+ else:
+ logging.debug("no exec args, creating new process group")
+ # No exec so we are the "child".
+ new_process_group()
+
+ # Reap children as init process
+ vdebug("installing local handler for SIGCHLD")
+ signal.signal(signal.SIGCHLD, sig_sigchld)
+
+ while True:
+ logging.info("init: waiting to reap zombies")
+ linux.pause()
+ # NOTREACHED
+
+ # Set (parent) signal handlers before any fork to avoid race
+ setup_init_signals()
+
+ logging.debug("forking to execute child")
+ g.child_pid = os.fork()
+ if g.child_pid == 0:
+ # In child, restore signals to default handling:
+ for snum in g.restore_signals:
+ signal.signal(snum, signal.SIG_DFL)
+
+ # XXX is a new pg right?
+ new_process_group()
+ logging.debug("child: executing '%s'", shlex.join(exec_args))
+ os.execvp(exec_args[0], exec_args)
+ # NOTREACHED
+
+ while True:
+ logging.info("parent: waiting for child pid %s to exit", g.child_pid)
+ waitpid("parent")
+
+
+def unshare(flags):
+ """Unshare into new namespaces."""
+ uflags = 0
+ for flag in flags:
+ if flag not in unshare_flags:
+ raise ValueError(f"unknown unshare flag '{flag}'")
+ uflags |= unshare_flags[flag]
+ new_pid = bool(uflags & linux.CLONE_NEWPID)
+ new_mnt = bool(uflags & linux.CLONE_NEWNS)
+
+ logging.debug("unshareing with flags: %s", linux.clone_flag_string(uflags))
+ linux.unshare(uflags)
+
+ if new_pid and not new_mnt:
+ try:
+ # If we are not creating new mount namspace, remount /proc private
+ # so that our mount of a new /proc doesn't affect parent namespace
+ logging.debug("remount /proc recursive private")
+ linux.mount("none", "/proc", None, linux.MS_REC | linux.MS_PRIVATE)
+ except OSError as error:
+ # EINVAL is OK b/c /proc not mounted may cause an error
+ if error.errno != errno.EINVAL:
+ raise
+ if new_mnt:
+ # Remount root as recursive private.
+ logging.debug("remount / recursive private")
+ linux.mount("none", "/", None, linux.MS_REC | linux.MS_PRIVATE)
+
+ # if new_pid:
+ # logging.debug("mount new /proc")
+ # linux.mount("proc", "/proc", "proc")
+
+ return new_pid
+
+
+def main():
+ #
+ # Parse CLI args.
+ #
+
+ ap = argparse.ArgumentParser()
+ ap.add_argument(
+ "-P",
+ "--no-proc-group",
+ action="store_true",
+ help="set to inherit the process group",
+ )
+ valid_flags = "".join(unshare_flags)
+ ap.add_argument(
+ "--unshare-flags",
+ help=(
+ f"string of unshare(1) flags. Supported values from '{valid_flags}'."
+ " 'm' will remount `/` recursive private. 'p' will remount /proc"
+ " and fork, and the child will be signaled to exit on exit of parent.."
+ ),
+ )
+ ap.add_argument(
+ "-v", dest="verbose", action="count", default=0, help="more -v's, more verbose"
+ )
+ ap.add_argument("rest", nargs=argparse.REMAINDER)
+ args = ap.parse_args()
+
+ #
+ # Setup logging.
+ #
+
+ level = logging.DEBUG if args.verbose else logging.INFO
+ if args.verbose > 1:
+ g.very_verbose = True
+ logging.basicConfig(
+ level=level, format="%(asctime)s mutini: %(levelname)s: %(message)s"
+ )
+
+ #
+ # Run program
+ #
+
+ status = 5
+ try:
+ new_pid = False
+ if args.unshare_flags:
+ new_pid = unshare(args.unshare_flags)
+
+ if g.orig_pid != 1 and not new_pid:
+ # Simply hold the namespaces
+ while True:
+ logging.info("holding namespace waiting to be signaled to exit")
+ linux.pause()
+ # NOTREACHED
+
+ be_init(not args.no_proc_group, args.rest)
+ # NOTREACHED
+ logging.critical("Exited from be_init!")
+ except KeyboardInterrupt:
+ logging.info("exiting (main), received KeyboardInterrupt in main")
+ status = 0x80 | signal.SIGINT
+ except Exception as error:
+ logging.info("exiting (main), do to exception %s", error, exc_info=True)
+
+ sys.exit(status)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/topotests/munet/native.py b/tests/topotests/munet/native.py
new file mode 100644
index 0000000..fecf709
--- /dev/null
+++ b/tests/topotests/munet/native.py
@@ -0,0 +1,2941 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# October 1 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021-2022, LabN Consulting, L.L.C.
+#
+# pylint: disable=protected-access
+"""A module that defines objects for standalone use."""
+import asyncio
+import errno
+import getpass
+import ipaddress
+import logging
+import os
+import random
+import re
+import shlex
+import socket
+import subprocess
+import time
+
+from . import cli
+from .base import BaseMunet
+from .base import Bridge
+from .base import Commander
+from .base import LinuxNamespace
+from .base import MunetError
+from .base import Timeout
+from .base import _async_get_exec_path
+from .base import _get_exec_path
+from .base import cmd_error
+from .base import commander
+from .base import fsafe_name
+from .base import get_exec_path_host
+from .config import config_subst
+from .config import config_to_dict_with_key
+from .config import find_matching_net_config
+from .config import find_with_kv
+from .config import merge_kind_config
+
+
+class L3ContainerNotRunningError(MunetError):
+ """Exception if no running container exists."""
+
+
+def get_loopback_ips(c, nid):
+ if ip := c.get("ip"):
+ if ip == "auto":
+ return [ipaddress.ip_interface("10.255.0.0/32") + nid]
+ if isinstance(ip, str):
+ return [ipaddress.ip_interface(ip)]
+ return [ipaddress.ip_interface(x) for x in ip]
+ return []
+
+
+def make_ip_network(net, inc):
+ n = ipaddress.ip_network(net)
+ return ipaddress.ip_network(
+ (n.network_address + inc * n.num_addresses, n.prefixlen)
+ )
+
+
+def make_ip_interface(ia, inc):
+ ia = ipaddress.ip_interface(ia)
+ # this turns into a /32 fix this
+ ia = ia + ia.network.num_addresses * inc
+ # IPv6
+ ia = ipaddress.ip_interface(str(ia).replace("/32", "/24").replace("/128", "/64"))
+ return ia
+
+
+def get_ip_network(c, brid, ipv6=False):
+ ip = c.get("ipv6" if ipv6 else "ip")
+ if ip and str(ip) != "auto":
+ try:
+ ifip = ipaddress.ip_interface(ip)
+ if ifip.ip == ifip.network.network_address:
+ return ifip.network
+ return ifip
+ except ValueError:
+ return ipaddress.ip_network(ip)
+ if ipv6:
+ return make_ip_interface("fc00::fe/64", brid)
+ return make_ip_interface("10.0.0.254/24", brid)
+
+
+def parse_pciaddr(devaddr):
+ comp = re.match(
+ "(?:([0-9A-Fa-f]{4}):)?([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2}).([0-7])", devaddr
+ ).groups()
+ if comp[0] is None:
+ comp[0] = "0000"
+ return [int(x, 16) for x in comp]
+
+
+def read_int_value(path):
+ return int(open(path, encoding="ascii").read())
+
+
+def read_str_value(path):
+ return open(path, encoding="ascii").read().strip()
+
+
+def read_sym_basename(path):
+ return os.path.basename(os.readlink(path))
+
+
+async def to_thread(func):
+ """to_thread for python < 3.9."""
+ try:
+ return await asyncio.to_thread(func)
+ except AttributeError:
+ logging.warning("Using backport to_thread")
+ return await asyncio.get_running_loop().run_in_executor(None, func)
+
+
+def convert_ranges_to_bitmask(ranges):
+ bitmask = 0
+ for r in ranges.split(","):
+ if "-" not in r:
+ bitmask |= 1 << int(r)
+ else:
+ x, y = (int(x) for x in r.split("-"))
+ for b in range(x, y + 1):
+ bitmask |= 1 << b
+ return bitmask
+
+
+class L2Bridge(Bridge):
+ """A linux bridge with no IP network address."""
+
+ def __init__(self, name=None, unet=None, logger=None, mtu=None, config=None):
+ """Create a linux Bridge."""
+ super().__init__(name=name, unet=unet, logger=logger, mtu=mtu)
+
+ self.config = config if config else {}
+
+ async def _async_delete(self):
+ self.logger.debug("%s: deleting", self)
+ await super()._async_delete()
+
+
+class L3Bridge(Bridge):
+ """A linux bridge with associated IP network address."""
+
+ def __init__(self, name=None, unet=None, logger=None, mtu=None, config=None):
+ """Create a linux Bridge."""
+ super().__init__(name=name, unet=unet, logger=logger, mtu=mtu)
+
+ self.config = config if config else {}
+
+ self.ip_interface = get_ip_network(self.config, self.id)
+ if hasattr(self.ip_interface, "network"):
+ self.ip_address = self.ip_interface.ip
+ self.ip_network = self.ip_interface.network
+ self.cmd_raises(f"ip addr add {self.ip_interface} dev {name}")
+ else:
+ self.ip_address = None
+ self.ip_network = self.ip_interface
+
+ self.logger.debug("%s: set IPv4 network address to %s", self, self.ip_interface)
+ self.cmd_raises("sysctl -w net.ipv4.ip_forward=1")
+
+ self.ip6_interface = None
+ if self.unet.ipv6_enable:
+ self.ip6_interface = get_ip_network(self.config, self.id, ipv6=True)
+ if hasattr(self.ip6_interface, "network"):
+ self.ip6_address = self.ip6_interface.ip
+ self.ip6_network = self.ip6_interface.network
+ self.cmd_raises(f"ip addr add {self.ip6_interface} dev {name}")
+ else:
+ self.ip6_address = None
+ self.ip6_network = self.ip6_interface
+
+ self.logger.debug(
+ "%s: set IPv6 network address to %s", self, self.ip_interface
+ )
+ self.cmd_raises("sysctl -w net.ipv6.conf.all.forwarding=1")
+
+ self.is_nat = self.config.get("nat", False)
+ if self.is_nat:
+ self.cmd_raises(
+ "iptables -t nat -A POSTROUTING "
+ f"-s {self.ip_network} ! -d {self.ip_network} "
+ f"! -o {self.name} -j MASQUERADE"
+ )
+
+ def get_intf_addr(self, ifname, ipv6=False):
+ # None is a valid interface, we have the same address for all interfaces
+ # just make sure they aren't asking for something we don't have.
+ if ifname is not None and ifname not in self.intfs:
+ return None
+ return self.ip6_interface if ipv6 else self.ip_interface
+
+ async def _async_delete(self):
+ self.logger.debug("%s: deleting", self)
+
+ if self.config.get("nat", False):
+ self.cmd_status(
+ "iptables -t nat -D POSTROUTING "
+ f"-s {self.ip_network} ! -d {self.ip_network} "
+ f"! -o {self.name} -j MASQUERADE"
+ )
+ await super()._async_delete()
+
+
+class NodeMixin:
+ """Node attributes and functionality."""
+
+ next_ord = 1
+
+ @classmethod
+ def _get_next_ord(cls):
+ # Do not use `cls` here b/c that makes the variable class specific
+ n = L3NodeMixin.next_ord
+ L3NodeMixin.next_ord = n + 1
+ return n
+
+ def __init__(self, *args, config=None, **kwargs):
+ """Create a Node."""
+ super().__init__(*args, **kwargs)
+
+ self.config = config if config else {}
+ config = self.config
+
+ self.id = int(config["id"]) if "id" in config else self._get_next_ord()
+
+ self.cmd_p = None
+ self.container_id = None
+ self.cleanup_called = False
+
+ # Clear and create rundir early
+ assert self.unet is not None
+ self.rundir = self.unet.rundir.joinpath(self.name)
+ commander.cmd_raises(f"rm -rf {self.rundir}")
+ commander.cmd_raises(f"mkdir -p {self.rundir}")
+
+ def _shebang_prep(self, config_key):
+ cmd = self.config.get(config_key, "").strip()
+ if not cmd:
+ return []
+
+ script_name = fsafe_name(config_key)
+
+ # shell_cmd is a union and can be boolean or string
+ shell_cmd = self.config.get("shell", "/bin/bash")
+ if not isinstance(shell_cmd, str):
+ if shell_cmd:
+ # i.e., "shell: true"
+ shell_cmd = "/bin/bash"
+ else:
+ # i.e., "shell: false"
+ shell_cmd = ""
+
+ # If we have a shell_cmd then we create a cleanup_cmds file in run_cmd
+ # and volume mounted it
+ if shell_cmd:
+ # Create cleanup cmd file
+ cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname))
+ cmd = cmd.replace("%RUNDIR%", str(self.rundir))
+ cmd = cmd.replace("%NAME%", str(self.name))
+ cmd += "\n"
+
+ # Write out our cleanup cmd file at this time too.
+ cmdpath = os.path.join(self.rundir, f"{script_name}.shebang")
+ with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile:
+ cmdfile.write(f"#!{shell_cmd}\n")
+ cmdfile.write(cmd)
+ cmdfile.flush()
+ commander.cmd_raises(f"chmod 755 {cmdpath}")
+
+ if self.container_id:
+ # XXX this counts on it being mounted in container, ugly
+ cmds = [f"/tmp/{script_name}.shebang"]
+ else:
+ cmds = [cmdpath]
+ else:
+ cmds = []
+ if isinstance(cmd, str):
+ cmds.extend(shlex.split(cmd))
+ else:
+ cmds.extend(cmd)
+ cmds = [
+ x.replace("%CONFIGDIR%", str(self.unet.config_dirname)) for x in cmds
+ ]
+ cmds = [x.replace("%RUNDIR%", str(self.rundir)) for x in cmds]
+ cmds = [x.replace("%NAME%", str(self.name)) for x in cmds]
+
+ return cmds
+
+ async def _async_shebang_cmd(self, config_key, warn=True):
+ cmds = self._shebang_prep(config_key)
+ if not cmds:
+ return 0
+
+ rc, o, e = await self.async_cmd_status(cmds, warn=warn)
+ if not rc and warn and (o or e):
+ self.logger.info(
+ f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e)
+ )
+ elif rc and warn:
+ self.logger.warning(
+ f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e)
+ )
+ else:
+ self.logger.debug(
+ f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e)
+ )
+
+ return rc
+
+ def has_run_cmd(self) -> bool:
+ return bool(self.config.get("cmd", "").strip())
+
+ async def get_proc_child_pid(self, p):
+ # commander is right for both unshare inline (our proc pidns)
+ # and non-inline (root pidns).
+
+ # This doesn't work b/c we can't get back to the root pidns
+
+ rootcmd = self.unet.rootcmd
+ pgrep = rootcmd.get_exec_path("pgrep")
+ spid = str(p.pid)
+ for _ in Timeout(4):
+ if p.returncode is not None:
+ self.logger.debug("%s: proc %s exited before getting child", self, p)
+ return None
+
+ rc, o, e = await rootcmd.async_cmd_status(
+ [pgrep, "-o", "-P", spid], warn=False
+ )
+ if rc == 0:
+ return int(o.strip())
+
+ await asyncio.sleep(0.1)
+ self.logger.debug(
+ "%s: no child of proc %s: %s", self, p, cmd_error(rc, o, e)
+ )
+ self.logger.warning("%s: timeout getting child pid of proc %s", self, p)
+ return None
+
+ async def run_cmd(self):
+ """Run the configured commands for this node."""
+ self.logger.debug(
+ "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir)
+ )
+
+ cmds = self._shebang_prep("cmd")
+ if not cmds:
+ return
+
+ stdout = open(os.path.join(self.rundir, "cmd.out"), "wb")
+ stderr = open(os.path.join(self.rundir, "cmd.err"), "wb")
+ self.cmd_pid = None
+ self.cmd_p = await self.async_popen(
+ cmds,
+ stdin=subprocess.DEVNULL,
+ stdout=stdout,
+ stderr=stderr,
+ start_new_session=True, # allows us to signal all children to exit
+ )
+
+ # If our process is actually the child of an nsenter fetch its pid.
+ if self.nsenter_fork:
+ self.cmd_pid = await self.get_proc_child_pid(self.cmd_p)
+
+ self.logger.debug(
+ "%s: async_popen %s => %s (cmd_pid %s)",
+ self,
+ cmds,
+ self.cmd_p.pid,
+ self.cmd_pid,
+ )
+
+ self.pytest_hook_run_cmd(stdout, stderr)
+
+ return self.cmd_p
+
+ async def _async_cleanup_cmd(self):
+ """Run the configured cleanup commands for this node.
+
+ This function is called by subclass' async_cleanup_cmd
+ """
+ self.cleanup_called = True
+
+ return await self._async_shebang_cmd("cleanup-cmd")
+
+ def has_cleanup_cmd(self) -> bool:
+ return bool(self.config.get("cleanup-cmd", "").strip())
+
+ async def async_cleanup_cmd(self):
+ """Run the configured cleanup commands for this node."""
+ return await self._async_cleanup_cmd()
+
+ def has_ready_cmd(self) -> bool:
+ return bool(self.config.get("ready-cmd", "").strip())
+
+ async def async_ready_cmd(self):
+ """Run the configured ready commands for this node."""
+ return not await self._async_shebang_cmd("ready-cmd", warn=False)
+
+ def cmd_completed(self, future):
+ self.logger.debug("%s: cmd completed callback", self)
+ try:
+ status = future.result()
+ self.logger.debug(
+ "%s: node cmd_p completed result: %s cmd: %s", self, status, self.cmd_p
+ )
+ self.cmd_pid = None
+ self.cmd_p = None
+ except asyncio.CancelledError:
+ # Should we stop the container if we have one?
+ self.logger.debug("%s: node cmd_p.wait() canceled", future)
+
+ def pytest_hook_run_cmd(self, stdout, stderr):
+ """Handle pytest options related to running the node cmd.
+
+ This function does things such as launch tail'ing windows
+ on the given files if requested by the user.
+
+ Args:
+ stdout: file-like object with a ``name`` attribute, or a path to a file.
+ stderr: file-like object with a ``name`` attribute, or a path to a file.
+ """
+ if not self.unet:
+ return
+
+ outopt = self.unet.cfgopt.getoption("--stdout")
+ outopt = outopt if outopt is not None else ""
+ if outopt == "all" or self.name in outopt.split(","):
+ outname = stdout.name if hasattr(stdout, "name") else stdout
+ self.run_in_window(f"tail -F {outname}", title=f"O:{self.name}")
+
+ if stderr:
+ erropt = self.unet.cfgopt.getoption("--stderr")
+ erropt = erropt if erropt is not None else ""
+ if erropt == "all" or self.name in erropt.split(","):
+ errname = stderr.name if hasattr(stderr, "name") else stderr
+ self.run_in_window(f"tail -F {errname}", title=f"E:{self.name}")
+
+ def pytest_hook_open_shell(self):
+ if not self.unet:
+ return
+
+ gdbcmd = self.config.get("gdb-cmd")
+ shellopt = self.unet.cfgopt.getoption("--gdb", "")
+ should_gdb = gdbcmd and (shellopt == "all" or self.name in shellopt.split(","))
+ use_emacs = self.unet.cfgopt.getoption("--gdb-use-emacs", False)
+
+ if should_gdb and not use_emacs:
+ cmds = self.config.get("gdb-target-cmds", [])
+ for cmd in cmds:
+ gdbcmd += f" '-ex={cmd}'"
+
+ bps = self.unet.cfgopt.getoption("--gdb-breakpoints", "").split(",")
+ for bp in bps:
+ gdbcmd += f" '-ex=b {bp}'"
+
+ cmds = self.config.get("gdb-run-cmd", [])
+ for cmd in cmds:
+ gdbcmd += f" '-ex={cmd}'"
+
+ self.run_in_window(gdbcmd)
+ elif should_gdb and use_emacs:
+ gdbcmd = gdbcmd.replace("gdb ", "gdb -i=mi ")
+ ecbin = self.get_exec_path("emacsclient")
+ # output = self.cmd_raises(
+ # [ecbin, "--eval", f"(gdb \"{gdbcmd} -ex='p 123456'\")"]
+ # )
+ _ = self.cmd_raises([ecbin, "--eval", f'(gdb "{gdbcmd}")'])
+
+ # can't figure out how to wait until symbols are loaded, until we do we just
+ # have to wait "long enough" for the symbol load to finish :/
+ # for _ in range(100):
+ # output = self.cmd_raises(
+ # [
+ # ecbin,
+ # "--eval",
+ # f"gdb-first-prompt",
+ # ]
+ # )
+ # if output == "nil\n":
+ # break
+ # time.sleep(0.25)
+
+ time.sleep(10)
+
+ cmds = self.config.get("gdb-target-cmds", [])
+ for cmd in cmds:
+ # we may want to quote quotes in the cmd string
+ self.cmd_raises(
+ [
+ ecbin,
+ "--eval",
+ f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")',
+ ]
+ )
+
+ bps = self.unet.cfgopt.getoption("--gdb-breakpoints", "").split(",")
+ for bp in bps:
+ cmd = f"br {bp}"
+ self.cmd_raises(
+ [
+ ecbin,
+ "--eval",
+ f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")',
+ ]
+ )
+
+ cmds = self.config.get("gdb-run-cmds", [])
+ for cmd in cmds:
+ # we may want to quote quotes in the cmd string
+ self.cmd_raises(
+ [
+ ecbin,
+ "--eval",
+ f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")',
+ ]
+ )
+ gdbcmd += f" '-ex={cmd}'"
+
+ shellopt = self.unet.cfgopt.getoption("--shell")
+ shellopt = shellopt if shellopt else ""
+ if shellopt == "all" or self.name in shellopt.split(","):
+ self.run_in_window("bash")
+
+ async def _async_delete(self):
+ self.logger.debug("%s: NodeMixin sub-class _async_delete", self)
+
+ if self.cmd_p:
+ await self.async_cleanup_proc(self.cmd_p, self.cmd_pid)
+ self.cmd_p = None
+
+ # Next call users "cleanup_cmd:"
+ try:
+ if not self.cleanup_called:
+ await self.async_cleanup_cmd()
+ except Exception as error:
+ self.logger.warning(
+ "Got an error during delete from async_cleanup_cmd: %s", error
+ )
+
+ # delete the LinuxNamespace/InterfaceMixin
+ await super()._async_delete()
+
+
+class SSHRemote(NodeMixin, Commander):
+ """SSHRemote a node representing an ssh connection to something."""
+
+ def __init__(
+ self,
+ name,
+ server,
+ port=22,
+ user=None,
+ password=None,
+ idfile=None,
+ **kwargs,
+ ):
+ super().__init__(name, **kwargs)
+
+ self.logger.debug("%s: creating", self)
+
+ # Things done in LinuxNamepsace we need to replicate here.
+ self.rundir = self.unet.rundir.joinpath(self.name)
+ self.unet.cmd_raises(f"rm -rf {self.rundir}")
+ self.unet.cmd_raises(f"mkdir -p {self.rundir}")
+
+ self.mgmt_ip = None
+ self.mgmt_ip6 = None
+
+ self.port = port
+
+ if user:
+ self.user = user
+ elif "SUDO_USER" in os.environ:
+ self.user = os.environ["SUDO_USER"]
+ else:
+ self.user = getpass.getuser()
+ self.password = password
+ self.idfile = idfile
+
+ self.server = f"{self.user}@{server}"
+
+ # Setup our base `pre-cmd` values
+ #
+ # We maybe should add environment variable transfer here in particular
+ # MUNET_NODENAME. The problem is the user has to explicitly approve
+ # of SendEnv variables.
+ self.__base_cmd = [
+ get_exec_path_host("sudo"),
+ "-E",
+ f"-u{self.user}",
+ get_exec_path_host("ssh"),
+ ]
+ if port != 22:
+ self.__base_cmd.append(f"-p{port}")
+ self.__base_cmd.append("-q")
+ self.__base_cmd.append("-oStrictHostKeyChecking=no")
+ self.__base_cmd.append("-oUserKnownHostsFile=/dev/null")
+ if self.idfile:
+ self.__base_cmd.append(f"-i{self.idfile}")
+ # Would be nice but has to be accepted by server config so not very useful.
+ # self.__base_cmd.append("-oSendVar='TEST'")
+ self.__base_cmd_pty = list(self.__base_cmd)
+ self.__base_cmd_pty.append("-t")
+ self.__base_cmd.append(self.server)
+ self.__base_cmd_pty.append(self.server)
+ # self.set_pre_cmd(pre_cmd, pre_cmd_tty)
+
+ self.logger.info("%s: created", self)
+
+ def has_ready_cmd(self) -> bool:
+ return bool(self.config.get("ready-cmd", "").strip())
+
+ def _get_pre_cmd(self, use_str, use_pty, ns_only=False, **kwargs):
+ pre_cmd = []
+ if self.unet:
+ pre_cmd = self.unet._get_pre_cmd(False, use_pty, ns_only=False, **kwargs)
+ if ns_only:
+ return pre_cmd
+
+ # XXX grab the env from kwargs and add to podman exec
+ # env = kwargs.get("env", {})
+ if use_pty:
+ pre_cmd = pre_cmd + self.__base_cmd_pty
+ else:
+ pre_cmd = pre_cmd + self.__base_cmd
+ return shlex.join(pre_cmd) if use_str else list(pre_cmd)
+
+ def _get_cmd_as_list(self, cmd):
+ """Given a list or string return a list form for execution.
+
+ If cmd is a string then [cmd] is returned, for most other
+ node types ["bash", "-c", cmd] is returned but in our case
+ ssh is the shell.
+
+ Args:
+ cmd: list or string representing the command to execute.
+ str_shell: if True and `cmd` is a string then run the
+ command using bash -c
+ Returns:
+ list of commands to execute.
+ """
+ return [cmd] if isinstance(cmd, str) else cmd
+
+
+# Would maybe like to refactor this into L3 and Node
+class L3NodeMixin(NodeMixin):
+ """A linux namespace with IP attributes."""
+
+ def __init__(self, *args, unet=None, **kwargs):
+ """Create an L3Node."""
+ # logging.warning(
+ # "L3NodeMixin: config %s unet %s kwargs %s", config, unet, kwargs
+ # )
+ super().__init__(*args, unet=unet, **kwargs)
+
+ self.mgmt_ip = None # set in parser.py
+ self.mgmt_ip6 = None # set in parser.py
+ self.host_intfs = {}
+ self.phy_intfs = {}
+ self.phycount = 0
+ self.phy_odrivers = {}
+ self.tapmacs = {}
+
+ self.intf_tc_count = 0
+
+ # super().__init__(name=name, **kwargs)
+
+ self.mount_volumes()
+
+ # -----------------------
+ # Setup node's networking
+ # -----------------------
+ if not unet.ipv6_enable:
+ # Disable IPv6
+ self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=0")
+ self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
+ else:
+ self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=1")
+ self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=0")
+
+ self.next_p2p_network = ipaddress.ip_network(f"10.254.{self.id}.0/31")
+ self.next_p2p_network6 = ipaddress.ip_network(f"fcff:ffff:{self.id:02x}::/127")
+
+ self.loopback_ip = None
+ self.loopback_ips = get_loopback_ips(self.config, self.id)
+ self.loopback_ip = self.loopback_ips[0] if self.loopback_ips else None
+ if self.loopback_ip:
+ self.cmd_raises_nsonly(f"ip addr add {self.loopback_ip} dev lo")
+ self.cmd_raises_nsonly("ip link set lo up")
+ for i, ip in enumerate(self.loopback_ips[1:]):
+ self.cmd_raises_nsonly(f"ip addr add {ip} dev lo:{i}")
+
+ # -------------------
+ # Setup node's rundir
+ # -------------------
+
+ # Not host path based, but we assume same
+ self.set_ns_cwd(self.rundir)
+
+ # Save the namespace pid
+ with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f:
+ f.write(f"{self.pid}\n")
+
+ with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f:
+ f.write(f'{" ".join([str(x) for x in self.pids])}\n')
+
+ # Create a hosts file to map our name
+ hosts_file = os.path.join(self.rundir, "hosts.txt")
+ with open(hosts_file, "w", encoding="ascii") as hf:
+ hf.write(
+ f"""127.0.0.1\tlocalhost {self.name}
+::1\tip6-localhost ip6-loopback
+fe00::0\tip6-localnet
+ff00::0\tip6-mcastprefix
+ff02::1\tip6-allnodes
+ff02::2\tip6-allrouters
+"""
+ )
+ if hasattr(self, "bind_mount"):
+ self.bind_mount(hosts_file, "/etc/hosts")
+
+ async def console(
+ self,
+ concmd,
+ prompt=r"(^|\r?\n)[^#\$]*[#\$] ",
+ is_bourne=True,
+ user=None,
+ password=None,
+ expects=None,
+ sends=None,
+ use_pty=False,
+ will_echo=False,
+ logfile_prefix="console",
+ trace=True,
+ **kwargs,
+ ):
+ """Create a REPL (read-eval-print-loop) driving a console.
+
+ Args:
+ concmd: string or list to popen with, or an already open socket
+ prompt: the REPL prompt to look for, the function returns when seen
+ is_bourne: True if the console is a bourne shell
+ user: user name to log in with
+ password: password to log in with
+ expects: a list of regex other than the prompt, the standard user, or
+ password to look for. "ogin:" or "[Pp]assword:"r.
+ sends: what to send when an element of `expects` matches. Can be the
+ empty string to send nothing.
+ use_pty: true for pty based expect, otherwise uses popen (pipes/files)
+ will_echo: bash is buggy in that it echo's to non-tty unlike any other
+ sh/ksh, set this value to true if running back
+ logfile_prefix: prefix for 3 logfiles opened to track the console i/o
+ trace: trace the send/expect sequence
+ **kwargs: kwargs passed on the _spawn.
+ """
+ lfname = os.path.join(self.rundir, f"{logfile_prefix}-log.txt")
+ logfile = open(lfname, "a+", encoding="utf-8")
+ logfile.write("-- start logging for: '{}' --\n".format(concmd))
+
+ lfname = os.path.join(self.rundir, f"{logfile_prefix}-read-log.txt")
+ logfile_read = open(lfname, "a+", encoding="utf-8")
+ logfile_read.write("-- start read logging for: '{}' --\n".format(concmd))
+
+ lfname = os.path.join(self.rundir, f"{logfile_prefix}-send-log.txt")
+ logfile_send = open(lfname, "a+", encoding="utf-8")
+ logfile_send.write("-- start send logging for: '{}' --\n".format(concmd))
+
+ expects = [] if expects is None else expects
+ sends = [] if sends is None else sends
+ if user:
+ expects.append("ogin:")
+ sends.append(user + "\n")
+ if password is not None:
+ expects.append("assword:")
+ sends.append(password + "\n")
+ repl = await self.shell_spawn(
+ concmd,
+ prompt,
+ expects=expects,
+ sends=sends,
+ use_pty=use_pty,
+ will_echo=will_echo,
+ is_bourne=is_bourne,
+ logfile=logfile,
+ logfile_read=logfile_read,
+ logfile_send=logfile_send,
+ trace=trace,
+ **kwargs,
+ )
+ return repl
+
+ async def monitor(
+ self,
+ sockpath,
+ prompt=r"\(qemu\) ",
+ ):
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(sockpath)
+
+ pfx = os.path.basename(sockpath)
+
+ lfname = os.path.join(self.rundir, f"{pfx}-log.txt")
+ logfile = open(lfname, "a+", encoding="utf-8")
+ logfile.write("-- start logging for: '{}' --\n".format(sock))
+
+ lfname = os.path.join(self.rundir, f"{pfx}-read-log.txt")
+ logfile_read = open(lfname, "a+", encoding="utf-8")
+ logfile_read.write("-- start read logging for: '{}' --\n".format(sock))
+
+ p = self.spawn(sock, prompt, logfile=logfile, logfile_read=logfile_read)
+ from .base import ShellWrapper # pylint: disable=C0415
+
+ p.send("\n")
+ return ShellWrapper(p, prompt, None, will_echo=True, escape_ansi=True)
+
+ def mount_volumes(self):
+ for m in self.config.get("volumes", []):
+ if isinstance(m, str):
+ s = m.split(":", 1)
+ if len(s) == 1:
+ self.tmpfs_mount(s[0])
+ else:
+ spath = s[0]
+ if spath[0] == ".":
+ spath = os.path.abspath(
+ os.path.join(self.unet.config_dirname, spath)
+ )
+ self.bind_mount(spath, s[1])
+ continue
+ raise NotImplementedError("complex mounts for non-containers")
+
+ def get_ifname(self, netname):
+ for c in self.config["connections"]:
+ if c["to"] == netname:
+ return c["name"]
+ return None
+
+ def set_lan_addr(self, switch, cconf):
+ if ip := cconf.get("ip"):
+ ipaddr = ipaddress.ip_interface(ip)
+ assert ipaddr.version == 4
+ elif self.unet.autonumber and "ip" not in cconf:
+ self.logger.debug(
+ "%s: prefixlen of switch %s is %s",
+ self,
+ switch.name,
+ switch.ip_network.prefixlen,
+ )
+ n = switch.ip_network
+ ipaddr = ipaddress.ip_interface((n.network_address + self.id, n.prefixlen))
+ else:
+ ipaddr = None
+
+ if ip := cconf.get("ipv6"):
+ ip6addr = ipaddress.ip_interface(ip)
+ assert ipaddr.version == 6
+ elif self.unet.ipv6_enable and self.unet.autonumber and "ipv6" not in cconf:
+ self.logger.debug(
+ "%s: prefixlen of switch %s is %s",
+ self,
+ switch.name,
+ switch.ip6_network.prefixlen,
+ )
+ n = switch.ip6_network
+ ip6addr = ipaddress.ip_interface((n.network_address + self.id, n.prefixlen))
+ else:
+ ip6addr = None
+
+ dns_network = self.unet.topoconf.get("dns-network")
+ for ip in (ipaddr, ip6addr):
+ if not ip:
+ continue
+ ipcmd = "ip " if ip.version == 4 else "ip -6 "
+ if dns_network and dns_network == switch.name:
+ if ip.version == 4:
+ self.mgmt_ip = ip.ip
+ else:
+ self.mgmt_ip6 = ip.ip
+ ifname = cconf["name"]
+ self.set_intf_addr(ifname, ip)
+ self.logger.debug("%s: adding %s to lan intf %s", self, ip, ifname)
+ if not self.is_vm:
+ self.intf_ip_cmd(ifname, ipcmd + f"addr add {ip} dev {ifname}")
+ if hasattr(switch, "is_nat") and switch.is_nat:
+ swaddr = (
+ switch.ip_address if ip.version == 4 else switch.ip6_address
+ )
+ self.cmd_raises(ipcmd + f"route add default via {swaddr}")
+
+ def _set_p2p_addr(self, other, cconf, occonf, ipv6=False):
+ ipkey = "ipv6" if ipv6 else "ip"
+ ipaddr = ipaddress.ip_interface(cconf[ipkey]) if cconf.get(ipkey) else None
+ oipaddr = ipaddress.ip_interface(occonf[ipkey]) if occonf.get(ipkey) else None
+ self.logger.debug(
+ "%s: set_p2p_addr %s %s %s", self, other.name, ipaddr, oipaddr
+ )
+
+ if not ipaddr and not oipaddr:
+ if self.unet.autonumber:
+ if ipv6:
+ n = self.next_p2p_network6
+ self.next_p2p_network6 = make_ip_network(n, 1)
+ else:
+ n = self.next_p2p_network
+ self.next_p2p_network = make_ip_network(n, 1)
+
+ ipaddr = ipaddress.ip_interface(n)
+ oipaddr = ipaddress.ip_interface((ipaddr.ip + 1, n.prefixlen))
+ else:
+ return
+
+ if ipaddr:
+ ifname = cconf["name"]
+ self.set_intf_addr(ifname, ipaddr)
+ self.logger.debug("%s: adding %s to p2p intf %s", self, ipaddr, ifname)
+ if "physical" not in cconf and not self.is_vm:
+ self.intf_ip_cmd(ifname, f"ip addr add {ipaddr} dev {ifname}")
+
+ if oipaddr:
+ oifname = occonf["name"]
+ other.set_intf_addr(oifname, oipaddr)
+ self.logger.debug(
+ "%s: adding %s to other p2p intf %s", other, oipaddr, oifname
+ )
+ if "physical" not in occonf and not other.is_vm:
+ other.intf_ip_cmd(oifname, f"ip addr add {oipaddr} dev {oifname}")
+
+ def set_p2p_addr(self, other, cconf, occonf):
+ self._set_p2p_addr(other, cconf, occonf, ipv6=False)
+ if self.unet.ipv6_enable:
+ self._set_p2p_addr(other, cconf, occonf, ipv6=True)
+
+ async def add_host_intf(self, hname, lname, mtu=None):
+ if hname in self.host_intfs:
+ return
+ self.host_intfs[hname] = lname
+ self.unet.rootcmd.cmd_nostatus(f"ip link set {hname} down ")
+ self.unet.rootcmd.cmd_raises(f"ip link set {hname} netns {self.pid}")
+ self.cmd_raises(f"ip link set {hname} name {lname}")
+ if mtu:
+ self.cmd_raises(f"ip link set {lname} mtu {mtu}")
+ self.cmd_raises(f"ip link set {lname} up")
+
+ async def rem_host_intf(self, hname):
+ lname = self.host_intfs[hname]
+ self.cmd_raises(f"ip link set {lname} down")
+ self.cmd_raises(f"ip link set {lname} name {hname}")
+ self.cmd_raises(f"ip link set {hname} netns 1")
+ del self.host_intfs[hname]
+
+ async def add_phy_intf(self, devaddr, lname):
+ """Add a physical inteface (i.e. mv it to vfio-pci driver.
+
+ This is primarily useful for Qemu, but also for things like TREX or DPDK
+ """
+ if devaddr in self.phy_intfs:
+ return
+ self.phy_intfs[devaddr] = lname
+ index = len(self.phy_intfs)
+
+ _, _, off, fun = parse_pciaddr(devaddr)
+ doffset = off * 8 + fun
+
+ is_virtual = self.unet.rootcmd.path_exists(
+ f"/sys/bus/pci/devices/{devaddr}/physfn"
+ )
+ if is_virtual:
+ pfname = self.unet.rootcmd.cmd_raises(
+ f"ls -1 /sys/bus/pci/devices/{devaddr}/physfn/net"
+ ).strip()
+ pdevaddr = read_sym_basename(f"/sys/bus/pci/devices/{devaddr}/physfn")
+ _, _, poff, pfun = parse_pciaddr(pdevaddr)
+ poffset = poff * 8 + pfun
+
+ offset = read_int_value(
+ f"/sys/bus/pci/devices/{devaddr}/physfn/sriov_offset"
+ )
+ stride = read_int_value(
+ f"/sys/bus/pci/devices/{devaddr}/physfn/sriov_stride"
+ )
+ vf = (doffset - offset - poffset) // stride
+ mac = f"02:cc:cc:cc:{index:02x}:{self.id:02x}"
+ # Some devices require the parent to be up (e.g., ixbge)
+ self.unet.rootcmd.cmd_raises(f"ip link set {pfname} up")
+ self.unet.rootcmd.cmd_raises(f"ip link set {pfname} vf {vf} mac {mac}")
+ self.unet.rootcmd.cmd_status(f"ip link set {pfname} vf {vf} trust on")
+ self.tapmacs[devaddr] = mac
+
+ self.logger.info("Adding physical PCI device %s as %s", devaddr, lname)
+
+ # Get interface name and set to down if present
+ ec, ifname, _ = self.unet.rootcmd.cmd_status(
+ f"ls /sys/bus/pci/devices/{devaddr}/net/", warn=False
+ )
+ ifname = ifname.strip()
+ if not ec and ifname:
+ # XXX Should only do this is the device is up, and then likewise return it
+ # up on exit self.phy_intfs_hostname[devaddr] = ifname
+ self.logger.info(
+ "Setting physical PCI device %s named %s down", devaddr, ifname
+ )
+ self.unet.rootcmd.cmd_status(
+ f"ip link set {ifname} down 2> /dev/null || true"
+ )
+
+ # Get the current bound driver, and unbind
+ try:
+ driver = read_sym_basename(f"/sys/bus/pci/devices/{devaddr}/driver")
+ driver = driver.strip()
+ except Exception:
+ driver = ""
+ if driver:
+ if driver == "vfio-pci":
+ self.logger.info(
+ "Physical PCI device %s already bound to vfio-pci", devaddr
+ )
+ return
+ self.logger.info(
+ "Unbinding physical PCI device %s from driver %s", devaddr, driver
+ )
+ self.phy_odrivers[devaddr] = driver
+ self.unet.rootcmd.cmd_raises(
+ f"echo {devaddr} > /sys/bus/pci/drivers/{driver}/unbind"
+ )
+
+ # Add the device vendor and device id to vfio-pci in case it's the first time
+ vendor = read_str_value(f"/sys/bus/pci/devices/{devaddr}/vendor")
+ devid = read_str_value(f"/sys/bus/pci/devices/{devaddr}/device")
+ self.logger.info("Adding device IDs %s:%s to vfio-pci", vendor, devid)
+ ec, _, _ = self.unet.rootcmd.cmd_status(
+ f"echo {vendor} {devid} > /sys/bus/pci/drivers/vfio-pci/new_id", warn=False
+ )
+
+ if not self.unet.rootcmd.path_exists(f"/sys/bus/pci/driver/vfio-pci/{devaddr}"):
+ # Bind to vfio-pci if wasn't added with new_id
+ self.logger.info("Binding physical PCI device %s to vfio-pci", devaddr)
+ ec, _, _ = self.unet.rootcmd.cmd_status(
+ f"echo {devaddr} > /sys/bus/pci/drivers/vfio-pci/bind"
+ )
+
+ async def rem_phy_intf(self, devaddr):
+ """Remove a physical inteface (i.e. mv it away from vfio-pci driver.
+
+ This is primarily useful for Qemu, but also for things like TREX or DPDK
+ """
+ lname = self.phy_intfs.get(devaddr, "")
+ if lname:
+ del self.phy_intfs[devaddr]
+
+ # ifname = self.phy_intfs_hostname.get(devaddr, "")
+ # if ifname
+ # del self.phy_intfs_hostname[devaddr]
+
+ driver = self.phy_odrivers.get(devaddr, "")
+ if not driver:
+ self.logger.info(
+ "Physical PCI device %s was bound to vfio-pci on entry", devaddr
+ )
+ return
+
+ self.logger.info(
+ "Unbinding physical PCI device %s from driver vfio-pci", devaddr
+ )
+ self.unet.rootcmd.cmd_status(
+ f"echo {devaddr} > /sys/bus/pci/drivers/vfio-pci/unbind"
+ )
+
+ self.logger.info("Binding physical PCI device %s to driver %s", devaddr, driver)
+ ec, _, _ = self.unet.rootcmd.cmd_status(
+ f"echo {devaddr} > /sys/bus/pci/drivers/{driver}/bind"
+ )
+ if not ec:
+ del self.phy_odrivers[devaddr]
+
+ async def _async_delete(self):
+ self.logger.debug("%s: L3NodeMixin sub-class _async_delete", self)
+
+ # XXX do we need to run the cleanup command before these infra changes?
+
+ # remove any hostintf interfaces
+ for hname in list(self.host_intfs):
+ await self.rem_host_intf(hname)
+
+ # remove any hostintf interfaces
+ for devaddr in list(self.phy_intfs):
+ await self.rem_phy_intf(devaddr)
+
+ # delete the LinuxNamespace/InterfaceMixin
+ await super()._async_delete()
+
+
+class L3NamespaceNode(L3NodeMixin, LinuxNamespace):
+ """A namespace L3 node."""
+
+ def __init__(self, name, pid=True, **kwargs):
+ # logging.warning(
+ # "L3NamespaceNode: name %s MRO: %s kwargs %s",
+ # name,
+ # L3NamespaceNode.mro(),
+ # kwargs,
+ # )
+ super().__init__(name, pid=pid, **kwargs)
+ super().pytest_hook_open_shell()
+
+ async def _async_delete(self):
+ self.logger.debug("%s: deleting", self)
+ await super()._async_delete()
+
+
+class L3ContainerNode(L3NodeMixin, LinuxNamespace):
+ """An container (podman) based L3 node."""
+
+ def __init__(self, name, config, **kwargs):
+ """Create a Container Node."""
+ self.cont_exec_paths = {}
+ self.container_id = None
+ self.container_image = config["image"]
+ self.extra_mounts = []
+ assert self.container_image
+
+ self.cmd_p = None
+ self.__base_cmd = []
+ self.__base_cmd_pty = []
+
+ # don't we have a mutini or cat process?
+ super().__init__(
+ name=name,
+ config=config,
+ # pid=True,
+ # cgroup=True,
+ # private_mounts=["/sys/fs/cgroup:/sys/fs/cgroup"],
+ **kwargs,
+ )
+
+ @property
+ def is_container(self):
+ return True
+
+ def get_exec_path(self, binary):
+ """Return the full path to the binary executable inside the image.
+
+ `binary` :: binary name or list of binary names
+ """
+ return _get_exec_path(binary, self.cmd_status, self.cont_exec_paths)
+
+ async def async_get_exec_path(self, binary):
+ """Return the full path to the binary executable inside the image.
+
+ `binary` :: binary name or list of binary names
+ """
+ path = await _async_get_exec_path(
+ binary, self.async_cmd_status, self.cont_exec_paths
+ )
+ return path
+
+ def get_exec_path_host(self, binary):
+ """Return the full path to the binary executable on the host.
+
+ `binary` :: binary name or list of binary names
+ """
+ return get_exec_path_host(binary)
+
+ def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs):
+ if ns_only:
+ return super()._get_pre_cmd(
+ use_str, use_pty, ns_only=True, root_level=root_level, **kwargs
+ )
+ if not self.cmd_p:
+ if self.container_id:
+ s = f"{self}: Running command in namespace b/c container exited"
+ self.logger.warning("%s", s)
+ raise L3ContainerNotRunningError(s)
+ self.logger.debug("%s: Running command in namespace b/c no container", self)
+ return super()._get_pre_cmd(
+ use_str, use_pty, ns_only=True, root_level=root_level, **kwargs
+ )
+
+ # We need to enter our namespaces when running the podman command
+ pre_cmd = super()._get_pre_cmd(
+ False, use_pty, ns_only=True, root_level=root_level, **kwargs
+ )
+
+ # XXX grab the env from kwargs and add to podman exec
+ # env = kwargs.get("env", {})
+ if use_pty:
+ pre_cmd = pre_cmd + self.__base_cmd_pty
+ else:
+ pre_cmd = pre_cmd + self.__base_cmd
+ return shlex.join(pre_cmd) if use_str else pre_cmd
+
+ def tmpfs_mount(self, inner):
+ # eventually would be nice to support live mounting
+ assert not self.container_id
+ self.logger.debug("Mounting tmpfs on %s", inner)
+ self.extra_mounts.append(f"--mount=type=tmpfs,destination={inner}")
+
+ def bind_mount(self, outer, inner):
+ # eventually would be nice to support live mounting
+ assert not self.container_id
+ # First bind the mount in the parent this allows things like /etc/hosts to work
+ # correctly when running "nsonly" commands
+ super().bind_mount(outer, inner)
+ # Then arrange for binding in the container as well.
+ self.logger.debug("Bind mounting %s on %s", outer, inner)
+ if not self.test_nsonly("-e", outer):
+ self.cmd_raises_nsonly(f"mkdir -p {outer}")
+ self.extra_mounts.append(f"--mount=type=bind,src={outer},dst={inner}")
+
+ def mount_volumes(self):
+ args = []
+ for m in self.config.get("volumes", []):
+ if isinstance(m, str):
+ s = m.split(":", 1)
+ if len(s) == 1:
+ args.append("--mount=type=tmpfs,destination=" + m)
+ else:
+ spath = s[0]
+ spath = os.path.abspath(
+ os.path.join(
+ os.path.dirname(self.unet.config["config_pathname"]), spath
+ )
+ )
+ if not self.test_nsonly("-e", spath):
+ self.cmd_raises_nsonly(f"mkdir -p {spath}")
+ args.append(f"--mount=type=bind,src={spath},dst={s[1]}")
+ continue
+
+ for m in self.config.get("mounts", []):
+ margs = ["type=" + m["type"]]
+ for k, v in m.items():
+ if k == "type":
+ continue
+ if v:
+ if k in ("src", "source"):
+ v = os.path.abspath(
+ os.path.join(
+ os.path.dirname(self.unet.config["config_pathname"]), v
+ )
+ )
+ if not self.test_nsonly("-e", v):
+ self.cmd_raises_nsonly(f"mkdir -p {v}")
+ margs.append(f"{k}={v}")
+ else:
+ margs.append(f"{k}")
+ args.append("--mount=" + ",".join(margs))
+
+ if args:
+ # Need to work on a way to mount into live container too
+ self.extra_mounts += args
+
+ def has_run_cmd(self) -> bool:
+ return True
+
+ async def run_cmd(self):
+ """Run the configured commands for this node."""
+ self.logger.debug("%s: starting container", self.name)
+ self.logger.debug(
+ "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir)
+ )
+
+ self.container_id = f"{self.name}-{os.getpid()}"
+ proc_path = self.unet.proc_path if self.unet else "/proc"
+ cmds = [
+ get_exec_path_host("podman"),
+ "run",
+ f"--name={self.container_id}",
+ # f"--net=ns:/proc/{self.pid}/ns/net",
+ f"--net=ns:{proc_path}/{self.pid}/ns/net",
+ f"--hostname={self.name}",
+ f"--add-host={self.name}:127.0.0.1",
+ # We can't use --rm here b/c podman fails on "stop".
+ # u"--rm",
+ ]
+
+ if self.config.get("init", True):
+ cmds.append("--init")
+
+ if self.config.get("privileged", False):
+ cmds.append("--privileged")
+ # If we don't do this then the host file system is remounted read-only on
+ # exit!
+ cmds.append("--systemd=false")
+ else:
+ cmds.extend(
+ [
+ # "--cap-add=SYS_ADMIN",
+ "--cap-add=NET_ADMIN",
+ "--cap-add=NET_RAW",
+ ]
+ )
+
+ # Add volumes:
+ if self.extra_mounts:
+ cmds += self.extra_mounts
+
+ # Add environment variables:
+ envdict = self.config.get("env", {})
+ if envdict is None:
+ envdict = {}
+ for k, v in envdict.items():
+ cmds.append(f"--env={k}={v}")
+
+ # Update capabilities
+ cmds += [f"--cap-add={x}" for x in self.config.get("cap-add", [])]
+ cmds += [f"--cap-drop={x}" for x in self.config.get("cap-drop", [])]
+ # cmds += [f"--expose={x.split(':')[0]}" for x in self.config.get("ports", [])]
+ cmds += [f"--publish={x}" for x in self.config.get("ports", [])]
+
+ # Add extra flags from user:
+ if "podman" in self.config:
+ for x in self.config["podman"].get("extra-args", []):
+ cmds.append(x.strip())
+
+ # shell_cmd is a union and can be boolean or string
+ shell_cmd = self.config.get("shell", "/bin/bash")
+ if not isinstance(shell_cmd, str):
+ if shell_cmd:
+ shell_cmd = "/bin/bash"
+ else:
+ shell_cmd = ""
+
+ # Create shebang files, filled later on
+ for key in ("cleanup-cmd", "ready-cmd"):
+ shebang_cmd = self.config.get(key, "").strip()
+ if shell_cmd and shebang_cmd:
+ script_name = fsafe_name(key)
+ # Will write the file contents out when the command is run
+ shebang_cmdpath = os.path.join(self.rundir, f"{script_name}.shebang")
+ await self.async_cmd_raises_nsonly(f"touch {shebang_cmdpath}")
+ await self.async_cmd_raises_nsonly(f"chmod 755 {shebang_cmdpath}")
+ cmds += [
+ # How can we override this?
+ # u'--entrypoint=""',
+ f"--volume={shebang_cmdpath}:/tmp/{script_name}.shebang",
+ ]
+
+ cmd = self.config.get("cmd", "").strip()
+
+ # See if we have a custom update for this `kind`
+ if kind := self.config.get("kind", None):
+ if kind in kind_run_cmd_update:
+ cmds, cmd = await kind_run_cmd_update[kind](self, shell_cmd, cmds, cmd)
+
+ # Create running command file
+ if shell_cmd and cmd:
+ assert isinstance(cmd, str)
+ # make cmd \n terminated for script
+ cmd = cmd.rstrip()
+ cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname))
+ cmd = cmd.replace("%RUNDIR%", str(self.rundir))
+ cmd = cmd.replace("%NAME%", str(self.name))
+ cmd += "\n"
+ cmdpath = os.path.join(self.rundir, "cmd.shebang")
+ with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile:
+ cmdfile.write(f"#!{shell_cmd}\n")
+ cmdfile.write(cmd)
+ cmdfile.flush()
+ self.cmd_raises_nsonly(f"chmod 755 {cmdpath}")
+ cmds += [
+ # How can we override this?
+ # u'--entrypoint=""',
+ f"--volume={cmdpath}:/tmp/cmds.shebang",
+ self.container_image,
+ "/tmp/cmds.shebang",
+ ]
+ else:
+ # `cmd` is a direct run (no shell) cmd
+ cmds.append(self.container_image)
+ if cmd:
+ if isinstance(cmd, str):
+ cmds.extend(shlex.split(cmd))
+ else:
+ cmds.extend(cmd)
+
+ cmds = [
+ x.replace("%CONFIGDIR%", str(self.unet.config_dirname)) for x in cmds
+ ]
+ cmds = [x.replace("%RUNDIR%", str(self.rundir)) for x in cmds]
+ cmds = [x.replace("%NAME%", str(self.name)) for x in cmds]
+
+ stdout = open(os.path.join(self.rundir, "cmd.out"), "wb")
+ stderr = open(os.path.join(self.rundir, "cmd.err"), "wb")
+ # Using nsonly avoids using `podman exec` to execute the cmds.
+ self.cmd_p = await self.async_popen_nsonly(
+ cmds,
+ stdin=subprocess.DEVNULL,
+ stdout=stdout,
+ stderr=stderr,
+ start_new_session=True, # keeps main tty signals away from podman
+ )
+
+ self.logger.debug("%s: async_popen => %s", self, self.cmd_p.pid)
+
+ self.pytest_hook_run_cmd(stdout, stderr)
+
+ # ---------------------------------------
+ # Now let's wait until container shows up
+ # ---------------------------------------
+ timeout = Timeout(30)
+ while self.cmd_p.returncode is None and not timeout.is_expired():
+ o = await self.async_cmd_raises_nsonly(
+ f"podman ps -q -f name={self.container_id}"
+ )
+ if o.strip():
+ break
+ elapsed = int(timeout.elapsed())
+ if elapsed <= 3:
+ await asyncio.sleep(0.1)
+ else:
+ self.logger.info("%s: run_cmd taking more than %ss", self, elapsed)
+ await asyncio.sleep(1)
+ if self.cmd_p.returncode is not None:
+ # leave self.container_id set to cause exception on use
+ self.logger.warning(
+ "%s: run_cmd exited quickly (%ss) rc: %s",
+ self,
+ timeout.elapsed(),
+ self.cmd_p.returncode,
+ )
+ elif timeout.is_expired():
+ self.logger.critical(
+ "%s: timeout (%ss) waiting for container to start",
+ self.name,
+ timeout.elapsed(),
+ )
+ assert not timeout.is_expired()
+
+ #
+ # Set our precmd for executing in the container
+ #
+ self.__base_cmd = [
+ get_exec_path_host("podman"),
+ "exec",
+ f"-eMUNET_RUNDIR={self.unet.rundir}",
+ f"-eMUNET_NODENAME={self.name}",
+ "-i",
+ ]
+ self.__base_cmd_pty = list(self.__base_cmd) # copy list to pty
+ self.__base_cmd.append(self.container_id) # end regular list
+ self.__base_cmd_pty.append("-t") # add pty flags
+ self.__base_cmd_pty.append(self.container_id) # end pty list
+ # self.set_pre_cmd(self.__base_cmd, self.__base_cmd_pty) # set both pre_cmd
+
+ self.logger.info("%s: started container", self.name)
+
+ self.pytest_hook_open_shell()
+
+ return self.cmd_p
+
+ async def async_cleanup_cmd(self):
+ """Run the configured cleanup commands for this node."""
+ self.cleanup_called = True
+
+ if "cleanup-cmd" not in self.config:
+ return
+
+ if not self.cmd_p:
+ self.logger.warning("async_cleanup_cmd: container no longer running")
+ return
+
+ return await self._async_cleanup_cmd()
+
+ def cmd_completed(self, future):
+ try:
+ log = self.logger.debug if self.deleting else self.logger.warning
+ n = future.result()
+ if self.deleting:
+ log("contianer `cmd:` result: %s", n)
+ else:
+ log(
+ "contianer `cmd:` exited early, "
+ "try adding `tail -f /dev/null` to `cmd:`, result: %s",
+ n,
+ )
+ except asyncio.CancelledError as error:
+ # Should we stop the container if we have one? or since we are canceled
+ # we know we will be deleting soon?
+ self.logger.warning(
+ "node container cmd wait() canceled: %s:%s", future, error
+ )
+ self.cmd_p = None
+
+ async def _async_delete(self):
+ self.logger.debug("%s: deleting", self)
+
+ if contid := self.container_id:
+ try:
+ if not self.cleanup_called:
+ self.logger.debug("calling user cleanup cmd")
+ await self.async_cleanup_cmd()
+ except Exception as error:
+ self.logger.warning(
+ "Got an error during delete from async_cleanup_cmd: %s", error
+ )
+
+ # Clear the container_id field we want to act like a namespace now.
+ self.container_id = None
+
+ o = ""
+ e = ""
+ if self.cmd_p:
+ self.logger.debug("podman stop on container: %s", contid)
+ if (rc := self.cmd_p.returncode) is None:
+ rc, o, e = await self.async_cmd_status_nsonly(
+ [get_exec_path_host("podman"), "stop", "--time=2", contid]
+ )
+ if rc and rc < 128:
+ self.logger.warning(
+ "%s: podman stop on cmd failed: %s",
+ self,
+ cmd_error(rc, o, e),
+ )
+ else:
+ # It's gone
+ self.cmd_p = None
+
+ # now remove the container
+ self.logger.debug("podman rm on container: %s", contid)
+ rc, o, e = await self.async_cmd_status_nsonly(
+ [get_exec_path_host("podman"), "rm", contid]
+ )
+ if rc:
+ self.logger.warning(
+ "%s: podman rm failed: %s", self, cmd_error(rc, o, e)
+ )
+ else:
+ self.logger.debug(
+ "podman removed container %s: %s", contid, cmd_error(rc, o, e)
+ )
+
+ await super()._async_delete()
+
+
+class L3QemuVM(L3NodeMixin, LinuxNamespace):
+ """An VM (qemu) based L3 node."""
+
+ def __init__(self, name, config, **kwargs):
+ """Create a Container Node."""
+ self.cont_exec_paths = {}
+ self.launch_p = None
+ self.qemu_config = config["qemu"]
+ self.extra_mounts = []
+ assert self.qemu_config
+ self.cmdrepl = None
+ self.conrepl = None
+ self.is_kvm = False
+ self.monrepl = None
+ self.tapfds = {}
+ self.cpu_thread_map = {}
+
+ self.tapnames = {}
+
+ self.use_ssh = False
+ self.__base_cmd = []
+ self.__base_cmd_pty = []
+
+ super().__init__(name=name, config=config, pid=False, **kwargs)
+
+ self.sockdir = self.rundir.joinpath("s")
+ self.cmd_raises(f"mkdir -p {self.sockdir}")
+
+ self.qemu_config = config_subst(
+ self.qemu_config,
+ name=self.name,
+ rundir=os.path.join(self.rundir, self.name),
+ configdir=self.unet.config_dirname,
+ )
+ self.ssh_keyfile = self.qemu_config.get("sshkey")
+
+ @property
+ def is_vm(self):
+ return True
+
+ def __setup_ssh(self):
+ if not self.ssh_keyfile:
+ self.logger.warning("%s: No sshkey config", self)
+ return False
+ if not self.mgmt_ip and not self.mgmt_ip6:
+ self.logger.warning("%s: No mgmt IP to ssh to", self)
+ return False
+ mgmt_ip = self.mgmt_ip if self.mgmt_ip else self.mgmt_ip6
+
+ #
+ # Since we have a keyfile shouldn't need to sudo
+ # self.user = os.environ.get("SUDO_USER", "")
+ # if not self.user:
+ # self.user = getpass.getuser()
+ # self.__base_cmd = [
+ # get_exec_path_host("sudo"),
+ # "-E",
+ # f"-u{self.user}",
+ # get_exec_path_host("ssh"),
+ # ]
+ #
+ port = 22
+ self.__base_cmd = [get_exec_path_host("ssh")]
+ if port != 22:
+ self.__base_cmd.append(f"-p{port}")
+ self.__base_cmd.append("-i")
+ self.__base_cmd.append(self.ssh_keyfile)
+ self.__base_cmd.append("-q")
+ self.__base_cmd.append("-oStrictHostKeyChecking=no")
+ self.__base_cmd.append("-oUserKnownHostsFile=/dev/null")
+ # Would be nice but has to be accepted by server config so not very useful.
+ # self.__base_cmd.append("-oSendVar='TEST'")
+ self.__base_cmd_pty = list(self.__base_cmd)
+ self.__base_cmd_pty.append("-t")
+
+ user = self.qemu_config.get("sshuser", "root")
+ self.__base_cmd.append(f"{user}@{mgmt_ip}")
+ self.__base_cmd.append("--")
+ self.__base_cmd_pty.append(f"{user}@{mgmt_ip}")
+ # self.__base_cmd_pty.append("--")
+ return True
+
+ def _get_cmd_as_list(self, cmd):
+ """Given a list or string return a list form for execution.
+
+ If cmd is a string then [cmd] is returned, for most other
+ node types ["bash", "-c", cmd] is returned but in our case
+ ssh is the shell.
+
+ Args:
+ cmd: list or string representing the command to execute.
+ str_shell: if True and `cmd` is a string then run the
+ command using bash -c
+ Returns:
+ list of commands to execute.
+ """
+ if self.use_ssh and self.launch_p:
+ return [cmd] if isinstance(cmd, str) else cmd
+ return super()._get_cmd_as_list(cmd)
+
+ def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs):
+ if ns_only:
+ return super()._get_pre_cmd(
+ use_str, use_pty, ns_only=True, root_level=root_level, **kwargs
+ )
+
+ if not self.launch_p:
+ self.logger.debug("%s: Running command in namespace b/c no VM", self)
+ return super()._get_pre_cmd(
+ use_str, use_pty, ns_only=True, root_level=root_level, **kwargs
+ )
+
+ if not self.use_ssh:
+ self.logger.debug(
+ "%s: Running command in namespace b/c no SSH configured", self
+ )
+ return super()._get_pre_cmd(
+ use_str, use_pty, ns_only=True, root_level=root_level, **kwargs
+ )
+
+ pre_cmd = self.unet._get_pre_cmd(use_str, use_pty, ns_only=True)
+
+ # This is going to run in the process namespaces.
+ # We really want it to run in the munet namespace which will
+ # be different unless unshare_inline was used.
+ #
+ # XXX grab the env from kwargs and add to podman exec
+ # env = kwargs.get("env", {})
+ if use_pty:
+ pre_cmd = pre_cmd + self.__base_cmd_pty
+ else:
+ pre_cmd = pre_cmd + self.__base_cmd
+ return shlex.join(pre_cmd) if use_str else pre_cmd
+
+ async def moncmd(self):
+ """Uses internal REPL to send cmmand to qemu monitor and get reply."""
+
+ def tmpfs_mount(self, inner):
+ # eventually would be nice to support live mounting
+ self.logger.debug("Mounting tmpfs on %s", inner)
+ self.extra_mounts.append(("", inner, ""))
+
+ #
+ # bind_mount is actually being used to mount into the namespace
+ #
+ # def bind_mount(self, outer, inner):
+ # # eventually would be nice to support live mounting
+ # assert not self.container_id
+ # if self.test_host("-f", outer):
+ # self.logger.warning("Can't bind mount files with L3QemuVM: %s", outer)
+ # return
+ # self.logger.debug("Bind mounting %s on %s", outer, inner)
+ # if not self.test_host("-e", outer):
+ # self.cmd_raises(f"mkdir -p {outer}")
+ # self.extra_mounts.append((outer, inner, ""))
+
+ def mount_volumes(self):
+ """Mount volumes from the config."""
+ args = []
+ for m in self.config.get("volumes", []):
+ if not isinstance(m, str):
+ continue
+ s = m.split(":", 1)
+ if len(s) == 1:
+ args.append(("", s[0], ""))
+ else:
+ spath = s[0]
+ spath = os.path.abspath(
+ os.path.join(
+ os.path.dirname(self.unet.config["config_pathname"]), spath
+ )
+ )
+ if not self.test_nsonly("-e", spath):
+ self.cmd_raises_nsonly(f"mkdir -p {spath}")
+ args.append((spath, s[1], ""))
+
+ for m in self.config.get("mounts", []):
+ src = m.get("src", m.get("source", ""))
+ if src:
+ src = os.path.abspath(
+ os.path.join(
+ os.path.dirname(self.unet.config["config_pathname"]), src
+ )
+ )
+ if not self.test_nsonly("-e", src):
+ self.cmd_raises_nsonly(f"mkdir -p {src}")
+ dst = m.get("dst", m.get("destination"))
+ assert dst, "destination path required for mount"
+
+ margs = []
+ for k, v in m.items():
+ if k in ["destination", "dst", "source", "src"]:
+ continue
+ if k == "type":
+ assert v in ["bind", "tmpfs"]
+ continue
+ if not v:
+ margs.append(k)
+ else:
+ margs.append(f"{k}={v}")
+ args.append((src, dst, ",".join(margs)))
+
+ if args:
+ self.extra_mounts += args
+
+ async def run_cmd(self):
+ """Run the configured commands for this node inside VM."""
+ self.logger.debug(
+ "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir)
+ )
+
+ cmd = self.config.get("cmd", "").strip()
+ if not cmd:
+ self.logger.debug("%s: no `cmd` to run", self)
+ return None
+
+ shell_cmd = self.config.get("shell", "/bin/bash")
+ if not isinstance(shell_cmd, str):
+ if shell_cmd:
+ shell_cmd = "/bin/bash"
+ else:
+ shell_cmd = ""
+
+ if shell_cmd:
+ cmd = cmd.rstrip()
+ cmd = f"#!{shell_cmd}\n" + cmd
+ cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname))
+ cmd = cmd.replace("%RUNDIR%", str(self.rundir))
+ cmd = cmd.replace("%NAME%", str(self.name))
+ cmd += "\n"
+
+ # Write a copy to the rundir
+ cmdpath = os.path.join(self.rundir, "cmd.shebang")
+ with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile:
+ cmdfile.write(cmd)
+ commander.cmd_raises(f"chmod 755 {cmdpath}")
+
+ # Now write a copy inside the VM
+ self.conrepl.cmd_status("cat > /tmp/cmd.shebang << EOF\n" + cmd + "\nEOF")
+ self.conrepl.cmd_status("chmod 755 /tmp/cmd.shebang")
+ cmds = "/tmp/cmd.shebang"
+ else:
+ cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname))
+ cmd = cmd.replace("%RUNDIR%", str(self.rundir))
+ cmd = cmd.replace("%NAME%", str(self.name))
+ cmds = cmd
+
+ # class future_proc:
+ # """Treat awaitable minimally as a proc."""
+ # def __init__(self, aw):
+ # self.aw = aw
+ # # XXX would be nice to have a real value here
+ # self.returncode = 0
+ # async def wait(self):
+ # if self.aw:
+ # return await self.aw
+ # return None
+
+ class now_proc:
+ """Treat awaitable minimally as a proc."""
+
+ def __init__(self, output):
+ self.output = output
+ self.returncode = 0
+
+ async def wait(self):
+ return self.output
+
+ if self.cmdrepl:
+ # self.cmd_p = future_proc(
+ # # We need our own console here b/c this is async and not returning
+ # # immediately
+ # # self.cmdrepl.run_command(cmds, timeout=120, async_=True)
+ # self.cmdrepl.run_command(cmds, timeout=120)
+ # )
+
+ # When run_command supports async_ arg we can use the above...
+ self.cmd_p = now_proc(self.cmdrepl.run_command(cmds, timeout=120))
+
+ # stdout and err both combined into logfile from the spawned repl
+ stdout = os.path.join(self.rundir, "_cmdcon-log.txt")
+ self.pytest_hook_run_cmd(stdout, None)
+ else:
+ # If we only have a console we can't run in parallel, so run to completion
+ self.cmd_p = now_proc(self.conrepl.run_command(cmds, timeout=120))
+
+ return self.cmd_p
+
+ # InterfaceMixin override
+ # We need a name unique in the shared namespace.
+ def get_ns_ifname(self, ifname):
+ return self.name + ifname
+
+ async def add_host_intf(self, hname, lname, mtu=None):
+ # L3QemuVM needs it's own add_host_intf for macvtap, We need to create the tap
+ # in the host then move that interface so that the ifindex/devfile are
+ # different.
+
+ if hname in self.host_intfs:
+ return
+
+ self.host_intfs[hname] = lname
+ index = len(self.host_intfs)
+
+ tapindex = self.unet.tapcount
+ self.unet.tapcount = self.unet.tapcount + 1
+
+ tapname = f"tap{tapindex}"
+ self.tapnames[hname] = tapname
+
+ mac = f"02:bb:bb:bb:{index:02x}:{self.id:02x}"
+ self.tapmacs[hname] = mac
+
+ self.unet.rootcmd.cmd_raises(
+ f"ip link add link {hname} name {tapname} type macvtap"
+ )
+ if mtu:
+ self.unet.rootcmd.cmd_raises(f"ip link set {tapname} mtu {mtu}")
+ self.unet.rootcmd.cmd_raises(f"ip link set {tapname} address {mac} up")
+ ifindex = self.unet.rootcmd.cmd_raises(
+ f"cat /sys/class/net/{tapname}/ifindex"
+ ).strip()
+ # self.unet.rootcmd.cmd_raises(f"ip link set {tapname} netns {self.pid}")
+
+ tapfile = f"/dev/tap{ifindex}"
+ fd = os.open(tapfile, os.O_RDWR)
+ self.tapfds[hname] = fd
+ self.logger.info(
+ "%s: Add host intf: created macvtap interface %s (%s) on %s fd %s",
+ self,
+ tapname,
+ tapfile,
+ hname,
+ fd,
+ )
+
+ async def rem_host_intf(self, hname):
+ tapname = self.tapnames[hname]
+ self.unet.rootcmd.cmd_raises(f"ip link set {tapname} down")
+ self.unet.rootcmd.cmd_raises(f"ip link delete {tapname} type macvtap")
+ del self.tapnames[hname]
+ del self.host_intfs[hname]
+
+ async def create_tap(self, index, ifname, mtu=None, driver="virtio-net-pci"):
+ # XXX we shouldn't be doign a tap on a bridge with a veth
+ # we should just be using a tap created earlier which was connected to the
+ # bridge. Except we need to handle the case of p2p qemu <-> namespace
+ #
+ ifname = self.get_ns_ifname(ifname)
+ brname = f"{self.name}br{index}"
+
+ tapindex = self.unet.tapcount
+ self.unet.tapcount += 1
+
+ mac = f"02:aa:aa:aa:{index:02x}:{self.id:02x}"
+ # nic = "tap,model=virtio-net-pci"
+ # qemu -net nic,model=virtio,addr=1a:46:0b:ca:bc:7b -net tap,fd=3 3<>/dev/tap11
+ self.cmd_raises(f"ip address flush dev {ifname}")
+ self.cmd_raises(f"ip tuntap add tap{tapindex} mode tap")
+ self.cmd_raises(f"ip link add name {brname} type bridge")
+ self.cmd_raises(f"ip link set dev {ifname} master {brname}")
+ self.cmd_raises(f"ip link set dev tap{tapindex} master {brname}")
+ if mtu:
+ self.cmd_raises(f"ip link set dev tap{tapindex} mtu {mtu}")
+ self.cmd_raises(f"ip link set dev {ifname} mtu {mtu}")
+ self.cmd_raises(f"ip link set dev tap{tapindex} up")
+ self.cmd_raises(f"ip link set dev {ifname} up")
+ self.cmd_raises(f"ip link set dev {brname} up")
+ dev = f"{driver},netdev=n{index},mac={mac}"
+ return [
+ "-netdev",
+ f"tap,id=n{index},ifname=tap{tapindex},script=no,downscript=no",
+ "-device",
+ dev,
+ ]
+
+ async def mount_mounts(self):
+ """Mount any shared directories."""
+ self.logger.info("Mounting shared directories")
+ con = self.conrepl
+ for i, m in enumerate(self.extra_mounts):
+ outer, mp, uargs = m
+ if not outer:
+ con.cmd_raises(f"mkdir -p {mp}")
+ margs = f"-o {uargs}" if uargs else ""
+ con.cmd_raises(f"mount {margs} -t tmpfs tmpfs {mp}")
+ continue
+
+ uargs = "" if uargs is None else uargs
+ margs = "trans=virtio"
+ if uargs:
+ margs += f",{uargs}"
+ self.logger.info("Mounting %s on %s with %s", outer, mp, margs)
+ con.cmd_raises(f"mkdir -p {mp}")
+ con.cmd_raises(f"mount -t 9p -o {margs} shared{i} {mp}")
+
+ async def renumber_interfaces(self):
+ """Re-number the interfaces.
+
+ After VM comes up need to renumber the interfaces now on the inside.
+ """
+ self.logger.info("Renumbering interfaces")
+ con = self.conrepl
+ con.cmd_raises("sysctl -w net.ipv4.ip_forward=1")
+ if self.unet.ipv6_enable:
+ self.cmd_raises("sysctl -w net.ipv6.conf.all.forwarding=1")
+ for ifname in sorted(self.intfs):
+ conn = find_with_kv(self.config.get("connections"), "name", ifname)
+ to = conn["to"]
+ switch = self.unet.switches.get(to)
+ mtu = conn.get("mtu")
+ if not mtu and switch:
+ mtu = switch.config.get("mtu")
+ if mtu:
+ con.cmd_raises(f"ip link set {ifname} mtu {mtu}")
+ con.cmd_raises(f"ip link set {ifname} up")
+ # In case there was some preconfig e.g., cloud-init
+ con.cmd_raises(f"ip -4 addr flush dev {ifname}")
+ sw_is_nat = switch and hasattr(switch, "is_nat") and switch.is_nat
+ if ifaddr := self.get_intf_addr(ifname, ipv6=False):
+ con.cmd_raises(f"ip addr add {ifaddr} dev {ifname}")
+ if sw_is_nat:
+ # In case there was some preconfig e.g., cloud-init
+ con.cmd_raises("ip route flush exact default")
+ con.cmd_raises(f"ip route add default via {switch.ip_address}")
+ if ifaddr := self.get_intf_addr(ifname, ipv6=True):
+ con.cmd_raises(f"ip -6 addr add {ifaddr} dev {ifname}")
+ if sw_is_nat:
+ # In case there was some preconfig e.g., cloud-init
+ con.cmd_raises("ip -6 route flush exact default")
+ con.cmd_raises(f"ip -6 route add default via {switch.ip6_address}")
+ con.cmd_raises("ip link set lo up")
+
+ if self.unet.cfgopt.getoption("--coverage"):
+ con.cmd_raises("mount -t debugfs none /sys/kernel/debug")
+
+ async def gather_coverage_data(self):
+ con = self.conrepl
+
+ gcda = "/sys/kernel/debug/gcov"
+ tmpdir = con.cmd_raises("mktemp -d").strip()
+ dest = "/gcov-data.tgz"
+ con.cmd_raises(rf"find {gcda} -type d -exec mkdir -p {tmpdir}/{{}} \;")
+ con.cmd_raises(
+ rf"find {gcda} -name '*.gcda' -exec sh -c 'cat < $0 > {tmpdir}/$0' {{}} \;"
+ )
+ con.cmd_raises(
+ rf"find {gcda} -name '*.gcno' -exec sh -c 'cp -d $0 {tmpdir}/$0' {{}} \;"
+ )
+ con.cmd_raises(rf"tar cf - -C {tmpdir} sys | gzip -c > {dest}")
+ con.cmd_raises(rf"rm -rf {tmpdir}")
+ self.logger.info("Saved coverage data in VM at %s", dest)
+ if self.use_ssh:
+ ldest = os.path.join(self.rundir, "gcov-data.tgz")
+ self.cmd_raises(["/bin/cat", dest], stdout=open(ldest, "wb"))
+ self.logger.info("Saved coverage data on host at %s", ldest)
+
+ async def _opencons(
+ self,
+ *cnames,
+ prompt=None,
+ is_bourne=True,
+ user="root",
+ password="",
+ expects=None,
+ sends=None,
+ timeout=-1,
+ ):
+ """Open consoles based on socket file names."""
+ timeo = Timeout(timeout)
+ cons = []
+ for cname in cnames:
+ sockpath = os.path.join(self.sockdir, cname)
+ connected = False
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ while self.launch_p.returncode is None and not timeo.is_expired():
+ try:
+ sock.connect(sockpath)
+ connected = True
+ break
+ except OSError as error:
+ if error.errno == errno.ENOENT:
+ self.logger.debug("waiting for console socket: %s", sockpath)
+ else:
+ self.logger.warning(
+ "can't open console socket: %s", error.strerror
+ )
+ raise
+ elapsed = int(timeo.elapsed())
+ if elapsed <= 3:
+ await asyncio.sleep(0.25)
+ else:
+ self.logger.info(
+ "%s: launch (qemu) taking more than %ss", self, elapsed
+ )
+ await asyncio.sleep(1)
+
+ if connected:
+ if prompt is None:
+ prompt = r"(^|\r\n)[^#\$]*[#\$] "
+ cons.append(
+ await self.console(
+ sock,
+ prompt=prompt,
+ is_bourne=is_bourne,
+ user=user,
+ password=password,
+ use_pty=False,
+ logfile_prefix=cname,
+ will_echo=True,
+ expects=expects,
+ sends=sends,
+ timeout=timeout,
+ trace=True,
+ )
+ )
+ elif self.launch_p.returncode is not None:
+ self.logger.warning(
+ "%s: launch (qemu) exited quickly (%ss) rc: %s",
+ self,
+ timeo.elapsed(),
+ self.launch_p.returncode,
+ )
+ raise Exception("Qemu launch exited early")
+ elif timeo.is_expired():
+ self.logger.critical(
+ "%s: timeout (%ss) waiting for qemu to start",
+ self,
+ timeo.elapsed(),
+ )
+ assert not timeo.is_expired()
+
+ return cons
+
+ async def set_cpu_affinity(self, afflist):
+ for i, aff in enumerate(afflist):
+ if not aff:
+ continue
+ # affmask = convert_ranges_to_bitmask(aff)
+ if i not in self.cpu_thread_map:
+ logging.warning("affinity %s given for missing vcpu %s", aff, i)
+ continue
+ logging.info("setting vcpu %s affinity to %s", i, aff)
+ tid = self.cpu_thread_map[i]
+ self.cmd_raises_nsonly(f"taskset -cp {aff} {tid}")
+
+ async def launch(self):
+ """Launch qemu."""
+ self.logger.info("%s: Launch Qemu", self)
+
+ qc = self.qemu_config
+ cc = qc.get("console", {})
+ bootd = "d" if "iso" in qc else "c"
+ # args = [get_exec_path_host("qemu-system-x86_64"),
+ # "-nodefaults", "-boot", bootd]
+ args = [get_exec_path_host("qemu-system-x86_64"), "-boot", bootd]
+
+ args += ["-machine", "q35"]
+
+ if qc.get("kvm"):
+ rc, _, e = await self.async_cmd_status_nsonly("ls -l /dev/kvm")
+ if rc:
+ self.logger.warning("Can't enable KVM no /dev/kvm: %s", e)
+ else:
+ # [args += ["-enable-kvm", "-cpu", "host"]
+ # uargs += ["-accel", "kvm", "-cpu", "Icelake-Server-v5"]
+ args += ["-accel", "kvm", "-cpu", "host"]
+
+ if ncpu := qc.get("ncpu"):
+ # args += ["-smp", f"sockets={ncpu}"]
+ args += ["-smp", f"cores={ncpu}"]
+ # args += ["-smp", f"{ncpu},sockets={ncpu},cores=1,threads=1"]
+
+ args.extend(["-m", str(qc.get("memory", "512M"))])
+
+ if "bios" in qc:
+ if qc["bios"] == "open-firmware":
+ args.extend(["-bios", "/usr/share/qemu/OVMF.fd"])
+ else:
+ args.extend(["-bios", qc["bios"]])
+ if "kernel" in qc:
+ args.extend(["-kernel", qc["kernel"]])
+ if "initrd" in qc:
+ args.extend(["-initrd", qc["initrd"]])
+ if "iso" in qc:
+ args.extend(["-cdrom", qc["iso"]])
+
+ # we only have append if we have a kernel
+ if "kernel" in qc:
+ args.append("-append")
+ root = qc.get("root", "/dev/ram0")
+ # Only 1 serial console the other ports (ttyS[123] hvc[01]) should have
+ # gettys in inittab
+ append = f"root={root} rw console=ttyS0"
+ if "cmdline-extra" in qc:
+ append += f" {qc['cmdline-extra']}"
+ args.append(append)
+
+ if "extra-args" in qc:
+ if isinstance(qc["extra-args"], list):
+ args.extend(qc["extra-args"])
+ else:
+ args.extend(shlex.split(qc["extra-args"]))
+
+ # Walk the list of connections in order so we attach them the same way
+ pass_fds = []
+ nnics = 0
+ pciaddr = 3
+ for index, conn in enumerate(self.config["connections"]):
+ devaddr = conn.get("physical", "")
+ hostintf = conn.get("hostintf", "")
+ if devaddr:
+ # if devaddr in self.tapmacs:
+ # mac = f",mac={self.tapmacs[devaddr]}"
+ # else:
+ # mac = ""
+ args += ["-device", f"vfio-pci,host={devaddr},addr={pciaddr}"]
+ elif hostintf:
+ fd = self.tapfds[hostintf]
+ mac = self.tapmacs[hostintf]
+ args += [
+ "-nic",
+ f"tap,model=virtio-net-pci,mac={mac},fd={fd},addr={pciaddr}",
+ ]
+ pass_fds.append(fd)
+ nnics += 1
+ elif not hostintf:
+ driver = conn.get("driver", "virtio-net-pci")
+ mtu = conn.get("mtu")
+ if not mtu and conn["to"] in self.unet.switches:
+ mtu = self.unet.switches[conn["to"]].config.get("mtu")
+ tapargs = await self.create_tap(
+ index, conn["name"], mtu=mtu, driver=driver
+ )
+ tapargs[-1] += f",addr={pciaddr}"
+ args += tapargs
+ nnics += 1
+ pciaddr += 1
+ if not nnics:
+ args += ["-nic", "none"]
+
+ dtpl = qc.get("disk-template")
+ diskpath = disk = qc.get("disk")
+ if dtpl and not disk:
+ disk = qc["disk"] = f"{self.name}-{os.path.basename(dtpl)}"
+ diskpath = os.path.join(self.rundir, disk)
+ if self.path_exists(diskpath):
+ logging.debug("Disk '%s' file exists, using.", diskpath)
+ else:
+ dtplpath = os.path.abspath(
+ os.path.join(
+ os.path.dirname(self.unet.config["config_pathname"]), dtpl
+ )
+ )
+ logging.info("Create disk '%s' from template '%s'", diskpath, dtplpath)
+ self.cmd_raises(
+ f"qemu-img create -f qcow2 -F qcow2 -b {dtplpath} {diskpath}"
+ )
+
+ if diskpath:
+ args.extend(
+ ["-drive", f"file={diskpath},if=none,id=sata-disk0,format=qcow2"]
+ )
+ args.extend(["-device", "ahci,id=ahci"])
+ args.extend(["-device", "ide-hd,bus=ahci.0,drive=sata-disk0"])
+
+ use_stdio = cc.get("stdio", True)
+ has_cmd = self.config.get("cmd")
+ use_cmdcon = has_cmd and use_stdio
+
+ #
+ # Any extra serial/console ports beyond thw first, require entries in
+ # inittab to have getty running on them, modify inittab
+ #
+ # Use -serial stdio for output only, and as the first serial console
+ # which kernel uses for printk, as it has serious issues with dropped
+ # input chars for some reason.
+ #
+ # 4 serial ports (max), we'll add extra ports using virtual consoles.
+ _sd = self.sockdir
+ if use_stdio:
+ args += ["-serial", "stdio"]
+ args += ["-serial", f"unix:{_sd}/_console,server,nowait"]
+ if use_cmdcon:
+ args += [
+ "-serial",
+ f"unix:{_sd}/_cmdcon,server,nowait",
+ ]
+ args += [
+ "-serial",
+ f"unix:{_sd}/console,server,nowait",
+ # A 2 virtual consoles - /dev/hvc[01]
+ # Requires CONFIG_HVC_DRIVER=y CONFIG_VIRTIO_CONSOLE=y
+ "-device",
+ "virtio-serial", # serial console bus
+ "-chardev",
+ f"socket,path={_sd}/vcon0,server=on,wait=off,id=vcon0",
+ "-chardev",
+ f"socket,path={_sd}/vcon1,server=on,wait=off,id=vcon1",
+ "-device",
+ "virtconsole,chardev=vcon0",
+ "-device",
+ "virtconsole,chardev=vcon1",
+ # 2 monitors
+ "-monitor",
+ f"unix:{_sd}/_monitor,server,nowait",
+ "-monitor",
+ f"unix:{_sd}/monitor,server,nowait",
+ "-gdb",
+ f"unix:{_sd}/gdbserver,server,nowait",
+ ]
+
+ for i, m in enumerate(self.extra_mounts):
+ args += [
+ "-virtfs",
+ f"local,path={m[0]},mount_tag=shared{i},security_model=passthrough",
+ ]
+
+ args += ["-nographic"]
+
+ #
+ # Launch Qemu
+ #
+
+ stdout = open(os.path.join(self.rundir, "qemu.out"), "wb")
+ stderr = open(os.path.join(self.rundir, "qemu.err"), "wb")
+ self.launch_p = await self.async_popen(
+ args,
+ stdin=subprocess.DEVNULL,
+ stdout=stdout,
+ stderr=stderr,
+ pass_fds=pass_fds,
+ # We don't need this here b/c we are only ever running qemu and that's all
+ # we need to kill for cleanup
+ # XXX reconcile this
+ start_new_session=True, # allows us to signal all children to exit
+ )
+
+ self.pytest_hook_run_cmd(stdout, stderr)
+
+ # We've passed these on, so don't need these open here anymore.
+ for fd in pass_fds:
+ os.close(fd)
+
+ self.logger.debug("%s: async_popen => %s", self, self.launch_p.pid)
+
+ confiles = ["_console"]
+ if use_cmdcon:
+ confiles.append("_cmdcon")
+
+ #
+ # Connect to the console socket, retrying
+ #
+ prompt = cc.get("prompt")
+ cons = await self._opencons(
+ *confiles,
+ prompt=prompt,
+ is_bourne=not bool(prompt),
+ user=cc.get("user", "root"),
+ password=cc.get("password", ""),
+ expects=cc.get("expects"),
+ sends=cc.get("sends"),
+ timeout=int(cc.get("timeout", 60)),
+ )
+ self.conrepl = cons[0]
+ if use_cmdcon:
+ self.cmdrepl = cons[1]
+ self.monrepl = await self.monitor(os.path.join(self.sockdir, "_monitor"))
+
+ # the monitor output has super annoying ANSI escapes in it
+
+ output = self.monrepl.cmd_nostatus("info status")
+ self.logger.info("VM status: %s", output)
+
+ output = self.monrepl.cmd_nostatus("info kvm")
+ self.logger.info("KVM status: %s", output)
+
+ #
+ # Set thread affinity
+ #
+ output = self.monrepl.cmd_nostatus("info cpus")
+ matches = re.findall(r"CPU #(\d+): *thread_id=(\d+)", output)
+ self.cpu_thread_map = {int(k): int(v) for k, v in matches}
+ if cpuaff := self.qemu_config.get("cpu-affinity"):
+ await self.set_cpu_affinity(cpuaff)
+
+ self.is_kvm = "disabled" not in output
+
+ if qc.get("unix-os", True):
+ await self.renumber_interfaces()
+
+ if self.extra_mounts:
+ await self.mount_mounts()
+
+ self.use_ssh = bool(self.ssh_keyfile)
+ if self.use_ssh:
+ self.use_ssh = self.__setup_ssh()
+
+ self.pytest_hook_open_shell()
+
+ return self.launch_p
+
+ def launch_completed(self, future):
+ self.logger.debug("%s: launch (qemu) completed called", self)
+ self.use_ssh = False
+ try:
+ n = future.result()
+ self.logger.debug("%s: node launch (qemu) completed result: %s", self, n)
+ except asyncio.CancelledError as error:
+ self.logger.debug(
+ "%s: node launch (qemu) cmd wait() canceled: %s", future, error
+ )
+
+ async def cleanup_qemu(self):
+ """Launch qemu."""
+ if self.launch_p:
+ await self.async_cleanup_proc(self.launch_p)
+
+ async def async_cleanup_cmd(self):
+ """Run the configured cleanup commands for this node."""
+ self.cleanup_called = True
+
+ if "cleanup-cmd" not in self.config:
+ return
+
+ if not self.launch_p:
+ self.logger.warning("async_cleanup_cmd: qemu no longer running")
+ return
+
+ raise NotImplementedError("Needs to be like run_cmd")
+ # return await self._async_cleanup_cmd()
+
+ async def _async_delete(self):
+ self.logger.debug("%s: deleting", self)
+
+ # Need to cleanup early b/c it is running on the VM
+ if self.cmd_p:
+ await self.async_cleanup_proc(self.cmd_p)
+ self.cmd_p = None
+
+ try:
+ # Need to cleanup early b/c it is running on the VM
+ if not self.cleanup_called:
+ await self.async_cleanup_cmd()
+ except Exception as error:
+ self.logger.warning(
+ "Got an error during delete from async_cleanup_cmd: %s", error
+ )
+
+ try:
+ if not self.launch_p:
+ self.logger.warning("async_delete: qemu is not running")
+ else:
+ await self.cleanup_qemu()
+ except Exception as error:
+ self.logger.warning("%s: failued to cleanup qemu process: %s", self, error)
+
+ await super()._async_delete()
+
+
+class Munet(BaseMunet):
+ """Munet."""
+
+ def __init__(
+ self,
+ rundir=None,
+ config=None,
+ pid=True,
+ logger=None,
+ **kwargs,
+ ):
+ # logging.warning("Munet")
+
+ if not rundir:
+ rundir = "/tmp/munet"
+
+ if logger is None:
+ logger = logging.getLogger("munet.unet")
+
+ super().__init__("munet", pid=pid, rundir=rundir, logger=logger, **kwargs)
+
+ self.built = False
+ self.tapcount = 0
+
+ self.cmd_raises(f"mkdir -p {self.rundir} && chmod 755 {self.rundir}")
+ self.set_ns_cwd(self.rundir)
+
+ if not config:
+ config = {}
+ self.config = config
+ if "config_pathname" in config:
+ self.config_pathname = os.path.realpath(config["config_pathname"])
+ self.config_dirname = os.path.dirname(self.config_pathname)
+ else:
+ self.config_pathname = ""
+ self.config_dirname = ""
+
+ # Done in BaseMunet now
+ # # We need some way to actually get back to the root namespace
+ # if not self.isolated:
+ # self.rootcmd = commander
+ # else:
+ # spid = str(pid)
+ # nsflags = (f"--mount={self.proc_path / spid / 'ns/mnt'}",
+ # f"--net={self.proc_path / spid / 'ns/net'}",
+ # f"--uts={self.proc_path / spid / 'ns/uts'}",
+ # f"--ipc={self.proc_path / spid / 'ns/ipc'}",
+ # f"--cgroup={self.proc_path / spid / 'ns/cgroup'}",
+ # f"--pid={self.proc_path / spid / 'ns/net'}",
+ # self.rootcmd = SharedNamespace("host", pid=1, nsflags=nsflags)
+
+ # Save the namespace pid
+ with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f:
+ f.write(f"{self.pid}\n")
+
+ with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f:
+ f.write(f'{" ".join([str(x) for x in self.pids])}\n')
+
+ hosts_file = os.path.join(self.rundir, "hosts.txt")
+ with open(hosts_file, "w", encoding="ascii") as hf:
+ hf.write(
+ f"""127.0.0.1\tlocalhost {self.name}
+::1\tip6-localhost ip6-loopback
+fe00::0\tip6-localnet
+ff00::0\tip6-mcastprefix
+ff02::1\tip6-allnodes
+ff02::2\tip6-allrouters
+"""
+ )
+ self.bind_mount(hosts_file, "/etc/hosts")
+
+ # Common CLI commands for any topology
+ cdict = {
+ "commands": [
+ {
+ "name": "pcap",
+ "format": "pcap NETWORK",
+ "help": (
+ "capture packets from NETWORK into file capture-NETWORK.pcap"
+ " the command is run within a new window which also shows"
+ " packet summaries. NETWORK can also be an interface specified"
+ " as HOST:INTF. To capture inside the host namespace."
+ ),
+ "exec": "tshark -s 9200 -i {0} -P -w capture-{0}.pcap",
+ "top-level": True,
+ "new-window": {"background": True},
+ },
+ {
+ "name": "nsterm",
+ "format": "nsterm HOST [HOST ...]",
+ "help": (
+ "open terminal[s] in the namespace only"
+ " (outside containers or VM), * for all"
+ ),
+ "exec": "bash",
+ "new-window": {"ns_only": True},
+ },
+ {
+ "name": "term",
+ "format": "term HOST [HOST ...]",
+ "help": "open terminal[s] (TMUX or XTerm) on HOST[S], * for all",
+ "exec": "bash",
+ "new-window": True,
+ },
+ {
+ "name": "xterm",
+ "format": "xterm HOST [HOST ...]",
+ "help": "open XTerm[s] on HOST[S], * for all",
+ "exec": "bash",
+ "new-window": {
+ "forcex": True,
+ },
+ },
+ {
+ "name": "sh",
+ "format": "[HOST ...] sh <SHELL-COMMAND>",
+ "help": "execute <SHELL-COMMAND> on hosts",
+ "exec": "{}",
+ },
+ {
+ "name": "shi",
+ "format": "[HOST ...] shi <INTERACTIVE-COMMAND>",
+ "help": "execute <INTERACTIVE-COMMAND> on HOST[s]",
+ "exec": "{}",
+ "interactive": True,
+ },
+ {
+ "name": "stdout",
+ "exec": (
+ "[ -e %RUNDIR%/qemu.out ] && tail -F %RUNDIR%/qemu.out "
+ "|| tail -F %RUNDIR%/cmd.out"
+ ),
+ "format": "stdout HOST [HOST ...]",
+ "help": "tail -f on the stdout of the qemu/cmd for this node",
+ "new-window": True,
+ },
+ {
+ "name": "stderr",
+ "exec": (
+ "[ -e %RUNDIR%/qemu.err ] && tail -F %RUNDIR%/qemu.err "
+ "|| tail -F %RUNDIR%/cmd.err"
+ ),
+ "format": "stderr HOST [HOST ...]",
+ "help": "tail -f on the stdout of the qemu/cmd for this node",
+ "new-window": True,
+ },
+ ]
+ }
+
+ cli.add_cli_config(self, cdict)
+
+ if "cli" in config:
+ cli.add_cli_config(self, config["cli"])
+
+ if "topology" not in self.config:
+ self.config["topology"] = {}
+
+ self.topoconf = self.config["topology"]
+ self.ipv6_enable = self.topoconf.get("ipv6-enable", False)
+
+ if self.isolated:
+ if not self.ipv6_enable:
+ # Disable IPv6
+ self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=0")
+ self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
+ else:
+ self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=1")
+ self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=0")
+
+ # we really need overlay, but overlay-layers (used by overlay-images)
+ # counts on things being present in overlay so this temp stuff doesn't work.
+ # if self.isolated:
+ # # Let's hide podman details
+ # self.tmpfs_mount("/var/lib/containers/storage/overlay-containers")
+
+ shellopt = self.cfgopt.getoption("--shell")
+ shellopt = shellopt if shellopt else ""
+ if shellopt == "all" or "." in shellopt.split(","):
+ self.run_in_window("bash")
+
+ def __del__(self):
+ """Catch case of build object but not async_deleted."""
+ if hasattr(self, "built"):
+ if not self.deleting:
+ logging.critical(
+ "Munet object deleted without calling `async_delete` for cleanup."
+ )
+ s = super()
+ if hasattr(s, "__del__"):
+ s.__del__(self)
+
+ async def _async_build(self, logger=None):
+ """Build the topology based on config."""
+ if self.built:
+ self.logger.warning("%s: is already built", self)
+ return
+
+ self.built = True
+
+ # Allow for all networks to be auto-numbered
+ topoconf = self.topoconf
+ autonumber = self.autonumber
+ ipv6_enable = self.ipv6_enable
+
+ # ---------------------------------------------
+ # Merge Kinds and perform variable substitution
+ # ---------------------------------------------
+
+ kinds = self.config.get("kinds", {})
+
+ for name, conf in config_to_dict_with_key(topoconf, "networks", "name").items():
+ if kind := conf.get("kind"):
+ if kconf := kinds[kind]:
+ conf = merge_kind_config(kconf, conf)
+ conf = config_subst(
+ conf, name=name, rundir=self.rundir, configdir=self.config_dirname
+ )
+ if "ip" not in conf and autonumber:
+ conf["ip"] = "auto"
+ if "ipv6" not in conf and autonumber and ipv6_enable:
+ conf["ipv6"] = "auto"
+ topoconf["networks"][name] = conf
+ self.add_network(name, conf, logger=logger)
+
+ for name, conf in config_to_dict_with_key(topoconf, "nodes", "name").items():
+ if kind := conf.get("kind"):
+ if kconf := kinds[kind]:
+ conf = merge_kind_config(kconf, conf)
+
+ config_to_dict_with_key(
+ conf, "env", "name"
+ ) # convert list of env objects to dict
+
+ conf = config_subst(
+ conf,
+ name=name,
+ rundir=os.path.join(self.rundir, name),
+ configdir=self.config_dirname,
+ )
+ topoconf["nodes"][name] = conf
+ self.add_l3_node(name, conf, logger=logger)
+
+ # ------------------
+ # Create connections
+ # ------------------
+
+ # Go through all connections and name them so they are sane to the user
+ # otherwise when we do p2p links the names/ords skip around based oddly
+ for name, node in self.hosts.items():
+ nconf = node.config
+ if "connections" not in nconf:
+ continue
+ nconns = []
+ for cconf in nconf["connections"]:
+ # Replace string only with a dictionary
+ if isinstance(cconf, str):
+ splitconf = cconf.split(":", 1)
+ cconf = {"to": splitconf[0]}
+ if len(splitconf) == 2:
+ cconf["name"] = splitconf[1]
+ # Allocate a name if not already assigned
+ if "name" not in cconf:
+ cconf["name"] = node.get_next_intf_name()
+ nconns.append(cconf)
+ nconf["connections"] = nconns
+
+ for name, node in self.hosts.items():
+ nconf = node.config
+ if "connections" not in nconf:
+ continue
+ for cconf in nconf["connections"]:
+ # Eventually can add support for unconnected intf here.
+ if "to" not in cconf:
+ continue
+ to = cconf["to"]
+ if to in self.switches:
+ switch = self.switches[to]
+ swconf = find_matching_net_config(name, cconf, switch.config)
+ await self.add_native_link(switch, node, swconf, cconf)
+ elif cconf["name"] not in node.intfs:
+ # Only add the p2p interface if not already there.
+ other = self.hosts[to]
+ oconf = find_matching_net_config(name, cconf, other.config)
+ await self.add_native_link(node, other, cconf, oconf)
+
+ @property
+ def autonumber(self):
+ return self.topoconf.get("networks-autonumber", False)
+
+ @autonumber.setter
+ def autonumber(self, value):
+ self.topoconf["networks-autonumber"] = bool(value)
+
+ async def add_native_link(self, node1, node2, c1=None, c2=None):
+ """Add a link between switch and node or 2 nodes."""
+ isp2p = False
+
+ c1 = {} if c1 is None else c1
+ c2 = {} if c2 is None else c2
+
+ if node1.name in self.switches:
+ assert node2.name in self.hosts
+ elif node2.name in self.switches:
+ assert node1.name in self.hosts
+ node1, node2 = node2, node1
+ c1, c2 = c2, c1
+ else:
+ # p2p link
+ assert node1.name in self.hosts
+ assert node1.name in self.hosts
+ isp2p = True
+
+ if "name" not in c1:
+ c1["name"] = node1.get_next_intf_name()
+ if1 = c1["name"]
+
+ if "name" not in c2:
+ c2["name"] = node2.get_next_intf_name()
+ if2 = c2["name"]
+
+ do_add_link = True
+ for n, c in ((node1, c1), (node2, c2)):
+ if "hostintf" in c:
+ await n.add_host_intf(c["hostintf"], c["name"], mtu=c.get("mtu"))
+ do_add_link = False
+ elif "physical" in c:
+ await n.add_phy_intf(c["physical"], c["name"])
+ do_add_link = False
+ if do_add_link:
+ assert "hostintf" not in c1
+ assert "hostintf" not in c2
+ assert "physical" not in c1
+ assert "physical" not in c2
+
+ if isp2p:
+ mtu1 = c1.get("mtu")
+ mtu2 = c2.get("mtu")
+ mtu = mtu1 if mtu1 else mtu2
+ if mtu1 and mtu2 and mtu1 != mtu2:
+ self.logger.error("mtus differ for add_link %s != %s", mtu1, mtu2)
+ else:
+ mtu = c2.get("mtu")
+
+ super().add_link(node1, node2, if1, if2, mtu=mtu)
+
+ if isp2p:
+ node1.set_p2p_addr(node2, c1, c2)
+ else:
+ node2.set_lan_addr(node1, c2)
+
+ if "physical" not in c1 and not node1.is_vm:
+ node1.set_intf_constraints(if1, **c1)
+ if "physical" not in c2 and not node2.is_vm:
+ node2.set_intf_constraints(if2, **c2)
+
+ def add_l3_node(self, name, config=None, **kwargs):
+ """Add a node to munet."""
+ if config and config.get("image"):
+ cls = L3ContainerNode
+ elif config and config.get("qemu"):
+ cls = L3QemuVM
+ elif config and config.get("server"):
+ cls = SSHRemote
+ kwargs["server"] = config["server"]
+ kwargs["port"] = int(config.get("server-port", 22))
+ if "ssh-identity-file" in config:
+ kwargs["idfile"] = config.get("ssh-identity-file")
+ if "ssh-user" in config:
+ kwargs["user"] = config.get("ssh-user")
+ if "ssh-password" in config:
+ kwargs["password"] = config.get("ssh-password")
+ else:
+ cls = L3NamespaceNode
+ return super().add_host(name, cls=cls, config=config, **kwargs)
+
+ def add_network(self, name, config=None, **kwargs):
+ """Add a l2 or l3 switch to munet."""
+ if config is None:
+ config = {}
+
+ cls = L3Bridge if config.get("ip") else L2Bridge
+ mtu = kwargs.get("mtu", config.get("mtu"))
+ return super().add_switch(name, cls=cls, config=config, mtu=mtu, **kwargs)
+
+ async def run(self):
+ tasks = []
+
+ hosts = self.hosts.values()
+ launch_nodes = [x for x in hosts if hasattr(x, "launch")]
+ launch_nodes = [x for x in launch_nodes if x.config.get("qemu")]
+ run_nodes = [x for x in hosts if hasattr(x, "has_run_cmd") and x.has_run_cmd()]
+ ready_nodes = [
+ x for x in hosts if hasattr(x, "has_ready_cmd") and x.has_ready_cmd()
+ ]
+
+ pcapopt = self.cfgopt.getoption("--pcap")
+ pcapopt = pcapopt if pcapopt else ""
+ if pcapopt == "all":
+ pcapopt = self.switches.keys()
+ if pcapopt:
+ for pcap in pcapopt.split(","):
+ if ":" in pcap:
+ host, intf = pcap.split(":")
+ pcap = f"{host}-{intf}"
+ host = self.hosts[host]
+ else:
+ host = self
+ intf = pcap
+ host.run_in_window(
+ f"tshark -s 9200 -i {intf} -P -w capture-{pcap}.pcap",
+ background=True,
+ title=f"cap:{pcap}",
+ )
+
+ if launch_nodes:
+ # would like a info when verbose here.
+ logging.debug("Launching nodes")
+ await asyncio.gather(*[x.launch() for x in launch_nodes])
+
+ # Watch for launched processes to exit
+ for node in launch_nodes:
+ task = asyncio.create_task(
+ node.launch_p.wait(), name=f"Node-{node.name}-launch"
+ )
+ task.add_done_callback(node.launch_completed)
+ tasks.append(task)
+
+ if run_nodes:
+ # would like a info when verbose here.
+ logging.debug("Running `cmd` on nodes")
+ await asyncio.gather(*[x.run_cmd() for x in run_nodes])
+
+ # Watch for run_cmd processes to exit
+ for node in run_nodes:
+ task = asyncio.create_task(node.cmd_p.wait(), name=f"Node-{node.name}-cmd")
+ task.add_done_callback(node.cmd_completed)
+ tasks.append(task)
+
+ # Wait for nodes to be ready
+ if ready_nodes:
+
+ async def wait_until_ready(x):
+ while not await x.async_ready_cmd():
+ logging.debug("Waiting for ready on: %s", x)
+ await asyncio.sleep(0.25)
+ logging.debug("%s is ready!", x)
+
+ logging.debug("Waiting for ready on nodes: %s", ready_nodes)
+ _, pending = await asyncio.wait(
+ [wait_until_ready(x) for x in ready_nodes], timeout=30
+ )
+ if pending:
+ logging.warning("Timeout waiting for ready: %s", pending)
+ for nr in pending:
+ nr.cancel()
+ raise asyncio.TimeoutError()
+ logging.debug("All nodes ready")
+
+ return tasks
+
+ async def _async_delete(self):
+ from .testing.util import async_pause_test # pylint: disable=C0415
+
+ self.logger.debug("%s: deleting.", self)
+
+ if self.cfgopt.getoption("--coverage"):
+ nodes = (
+ x for x in self.hosts.values() if hasattr(x, "gather_coverage_data")
+ )
+ try:
+ await asyncio.gather(*(x.gather_coverage_data() for x in nodes))
+ except Exception as error:
+ logging.warning("Error gathering coverage data: %s", error)
+
+ pause = bool(self.cfgopt.getoption("--pause-at-end"))
+ pause = pause or bool(self.cfgopt.getoption("--pause"))
+ if pause:
+ try:
+ await async_pause_test("Before MUNET delete")
+ except KeyboardInterrupt:
+ print("^C...continuing")
+ except Exception as error:
+ self.logger.error("\n...continuing after error: %s", error)
+
+ # XXX should we cancel launch and run tasks?
+
+ try:
+ await super()._async_delete()
+ except Exception as error:
+ self.logger.error("Error cleaning up: %s", error, exc_info=True)
+ raise
+
+
+async def run_cmd_update_ceos(node, shell_cmd, cmds, cmd):
+ cmd = cmd.strip()
+ if shell_cmd or cmd != "/sbin/init":
+ return cmds, cmd
+
+ #
+ # Add flash dir and mount it
+ #
+ flashdir = os.path.join(node.rundir, "flash")
+ node.cmd_raises_nsonly(f"mkdir -p {flashdir} && chmod 775 {flashdir}")
+ cmds += [f"--volume={flashdir}:/mnt/flash"]
+
+ #
+ # Startup config (if not present already)
+ #
+ if startup_config := node.config.get("startup-config", None):
+ dest = os.path.join(flashdir, "startup-config")
+ if os.path.exists(dest):
+ node.logger.info("Skipping copy of startup-config, already present")
+ else:
+ source = os.path.join(node.unet.config_dirname, startup_config)
+ node.cmd_raises_nsonly(f"cp {source} {dest} && chmod 664 {dest}")
+
+ #
+ # system mac address (if not present already
+ #
+ dest = os.path.join(flashdir, "system_mac_address")
+ if os.path.exists(dest):
+ node.logger.info("Skipping system-mac generation, already present")
+ else:
+ random_arista_mac = "00:1c:73:%02x:%02x:%02x" % (
+ random.randint(0, 255),
+ random.randint(0, 255),
+ random.randint(0, 255),
+ )
+ system_mac = node.config.get("system-mac", random_arista_mac)
+ with open(dest, "w", encoding="ascii") as f:
+ f.write(system_mac + "\n")
+ node.cmd_raises_nsonly(f"chmod 664 {dest}")
+
+ args = []
+
+ # Pass special args for the environment variables
+ if "env" in node.config:
+ args += [f"systemd.setenv={k}={v}" for k, v in node.config["env"].items()]
+
+ return cmds, [cmd] + args
+
+
+# XXX this is only used by the container code
+kind_run_cmd_update = {"ceos": run_cmd_update_ceos}
diff --git a/tests/topotests/munet/parser.py b/tests/topotests/munet/parser.py
new file mode 100644
index 0000000..4fc0c75
--- /dev/null
+++ b/tests/topotests/munet/parser.py
@@ -0,0 +1,374 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# September 30 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""A module that implements the standalone parser."""
+import asyncio
+import importlib.resources
+import json
+import logging
+import logging.config
+import os
+import subprocess
+import sys
+import tempfile
+
+from pathlib import Path
+
+
+try:
+ import jsonschema # pylint: disable=C0415
+ import jsonschema.validators # pylint: disable=C0415
+
+ from jsonschema.exceptions import ValidationError # pylint: disable=C0415
+except ImportError:
+ jsonschema = None
+
+from .config import list_to_dict_with_key
+from .native import Munet
+
+
+def get_schema():
+ if get_schema.schema is None:
+ with importlib.resources.path("munet", "munet-schema.json") as datapath:
+ search = [str(datapath.parent)]
+ get_schema.schema = get_config(basename="munet-schema", search=search)
+ return get_schema.schema
+
+
+get_schema.schema = None
+
+project_root_contains = [
+ ".git",
+ "pyproject.toml",
+ "tox.ini",
+ "setup.cfg",
+ "setup.py",
+ "pytest.ini",
+ ".projectile",
+]
+
+
+def is_project_root(path: Path) -> bool:
+
+ for contains in project_root_contains:
+ if path.joinpath(contains).exists():
+ return True
+ return False
+
+
+def find_project_root(config_path: Path, project_root=None):
+ if project_root is not None:
+ project_root = Path(project_root)
+ if project_root in config_path.parents:
+ return project_root
+ logging.warning(
+ "project_root %s is not a common ancestor of config file %s",
+ project_root,
+ config_path,
+ )
+ return config_path.parent
+ for ppath in config_path.parents:
+ if is_project_root(ppath):
+ return ppath
+ return config_path.parent
+
+
+def get_config(pathname=None, basename="munet", search=None, logf=logging.debug):
+
+ cwd = os.getcwd()
+
+ if not search:
+ search = [cwd]
+ elif isinstance(search, (str, Path)):
+ search = [search]
+
+ if pathname:
+ pathname = os.path.join(cwd, pathname)
+ if not os.path.exists(pathname):
+ raise FileNotFoundError(pathname)
+ else:
+ for d in search:
+ logf("%s", f'searching in "{d}" for "{basename}".{{yaml, toml, json}}')
+ for ext in ("yaml", "toml", "json"):
+ pathname = os.path.join(d, basename + "." + ext)
+ if os.path.exists(pathname):
+ logf("%s", f'Found "{pathname}"')
+ break
+ else:
+ continue
+ break
+ else:
+ raise FileNotFoundError(basename + ".{json,toml,yaml} in " + f"{search}")
+
+ _, ext = pathname.rsplit(".", 1)
+
+ if ext == "json":
+ config = json.load(open(pathname, encoding="utf-8"))
+ elif ext == "toml":
+ import toml # pylint: disable=C0415
+
+ config = toml.load(pathname)
+ elif ext == "yaml":
+ import yaml # pylint: disable=C0415
+
+ config = yaml.safe_load(open(pathname, encoding="utf-8"))
+ else:
+ raise ValueError("Filename does not end with (.json|.toml|.yaml)")
+
+ config["config_pathname"] = os.path.realpath(pathname)
+ return config
+
+
+def setup_logging(args, config_base="logconf"):
+ # Create rundir and arrange for future commands to run in it.
+
+ # Change CWD to the rundir prior to parsing config
+ old = os.getcwd()
+ os.chdir(args.rundir)
+ try:
+ search = [old]
+ with importlib.resources.path("munet", config_base + ".yaml") as datapath:
+ search.append(str(datapath.parent))
+
+ def logf(msg, *p, **k):
+ if args.verbose:
+ print("PRELOG: " + msg % p, **k, file=sys.stderr)
+
+ config = get_config(args.log_config, config_base, search, logf=logf)
+ pathname = config["config_pathname"]
+ del config["config_pathname"]
+
+ if "info_console" in config["handlers"]:
+ # mutest case
+ if args.verbose > 1:
+ config["handlers"]["console"]["level"] = "DEBUG"
+ config["handlers"]["info_console"]["level"] = "DEBUG"
+ elif args.verbose:
+ config["handlers"]["console"]["level"] = "INFO"
+ config["handlers"]["info_console"]["level"] = "DEBUG"
+ elif args.verbose:
+ # munet case
+ config["handlers"]["console"]["level"] = "DEBUG"
+
+ # add the rundir path to the filenames
+ for v in config["handlers"].values():
+ filename = v.get("filename")
+ if not filename:
+ continue
+ v["filename"] = os.path.join(args.rundir, filename)
+
+ logging.config.dictConfig(dict(config))
+ logging.info("Loaded logging config %s", pathname)
+
+ return config
+ finally:
+ os.chdir(old)
+
+
+def append_hosts_files(unet, netname):
+ if not netname:
+ return
+
+ entries = []
+ for name in ("munet", *list(unet.hosts)):
+ if name == "munet":
+ node = unet.switches[netname]
+ ifname = None
+ else:
+ node = unet.hosts[name]
+ if not hasattr(node, "_intf_addrs"):
+ continue
+ ifname = node.get_ifname(netname)
+
+ for b in (False, True):
+ ifaddr = node.get_intf_addr(ifname, ipv6=b)
+ if ifaddr and hasattr(ifaddr, "ip"):
+ entries.append((name, ifaddr.ip))
+
+ for name in ("munet", *list(unet.hosts)):
+ node = unet if name == "munet" else unet.hosts[name]
+ if not hasattr(node, "rundir"):
+ continue
+ with open(os.path.join(node.rundir, "hosts.txt"), "a+", encoding="ascii") as hf:
+ hf.write("\n")
+ for e in entries:
+ hf.write(f"{e[1]}\t{e[0]}\n")
+
+
+def validate_config(config, logger, args):
+ if jsonschema is None:
+ logger.debug("No validation w/o jsonschema module")
+ return True
+
+ old = os.getcwd()
+ if args:
+ os.chdir(args.rundir)
+
+ try:
+ validator = jsonschema.validators.Draft202012Validator(get_schema())
+ validator.validate(instance=config)
+ logger.debug("Validated %s", config["config_pathname"])
+ return True
+ except FileNotFoundError as error:
+ logger.info("No schema found: %s", error)
+ return False
+ except ValidationError as error:
+ logger.info("Validation failed: %s", error)
+ return False
+ finally:
+ if args:
+ os.chdir(old)
+
+
+def load_kinds(args, search=None):
+ # Change CWD to the rundir prior to parsing config
+ cwd = os.getcwd()
+ if args:
+ os.chdir(args.rundir)
+
+ args_config = args.kinds_config if args else None
+ try:
+ if search is None:
+ search = [cwd]
+ with importlib.resources.path("munet", "kinds.yaml") as datapath:
+ search.insert(0, str(datapath.parent))
+
+ configs = []
+ if args_config:
+ configs.append(get_config(args_config, "kinds", search=[]))
+ else:
+ # prefer directories at the front of the list
+ for kdir in search:
+ try:
+ configs.append(get_config(basename="kinds", search=[kdir]))
+ except FileNotFoundError:
+ continue
+
+ kinds = {}
+ for config in configs:
+ # XXX need to fix the issue with `connections: ["net0"]` not validating
+ # if jsonschema is not None:
+ # validator = jsonschema.validators.Draft202012Validator(get_schema())
+ # validator.validate(instance=config)
+
+ kinds_list = config.get("kinds", [])
+ kinds_dict = list_to_dict_with_key(kinds_list, "name")
+ if kinds_dict:
+ logging.info("Loading kinds config from %s", config["config_pathname"])
+ if "kinds" in kinds:
+ kinds["kinds"].update(**kinds_dict)
+ else:
+ kinds["kinds"] = kinds_dict
+
+ cli_list = config.get("cli", {}).get("commands", [])
+ if cli_list:
+ logging.info("Loading cli comands from %s", config["config_pathname"])
+ if "cli" not in kinds:
+ kinds["cli"] = {}
+ if "commands" not in kinds["cli"]:
+ kinds["cli"]["commands"] = []
+ kinds["cli"]["commands"].extend(cli_list)
+
+ return kinds
+ except FileNotFoundError as error:
+ # if we have kinds in args but the file doesn't exist, raise the error
+ if args_config is not None:
+ raise error
+ return {}
+ finally:
+ if args:
+ os.chdir(cwd)
+
+
+async def async_build_topology(
+ config=None,
+ logger=None,
+ rundir=None,
+ args=None,
+ unshare_inline=False,
+ pytestconfig=None,
+ search_root=None,
+ top_level_pidns=True,
+):
+
+ if not rundir:
+ rundir = tempfile.mkdtemp(prefix="unet")
+ subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True)
+
+ isolated = not args.host if args else True
+ if not config:
+ config = get_config(basename="munet")
+
+ # create search directories from common root if given
+ cpath = Path(config["config_pathname"]).absolute()
+ project_root = args.project_root if args else None
+ if not search_root:
+ search_root = find_project_root(cpath, project_root)
+ if not search_root:
+ search = [cpath.parent]
+ else:
+ search_root = Path(search_root).absolute()
+ if search_root in cpath.parents:
+ search = list(cpath.parents)
+ if remcount := len(search_root.parents):
+ search = search[0:-remcount]
+
+ # load kinds along search path and merge into config
+ kinds = load_kinds(args, search=search)
+ config_kinds_dict = list_to_dict_with_key(config.get("kinds", []), "name")
+ config["kinds"] = {**kinds.get("kinds", {}), **config_kinds_dict}
+
+ # mere CLI command from kinds into config as well.
+ kinds_cli_list = kinds.get("cli", {}).get("commands", [])
+ config_cli_list = config.get("cli", {}).get("commands", [])
+ if config_cli_list:
+ if kinds_cli_list:
+ config_cli_list.extend(list(kinds_cli_list))
+ elif kinds_cli_list:
+ if "cli" not in config:
+ config["cli"] = {}
+ if "commands" not in config["cli"]:
+ config["cli"]["commands"] = []
+ config["cli"]["commands"].extend(list(kinds_cli_list))
+
+ unet = Munet(
+ rundir=rundir,
+ config=config,
+ pytestconfig=pytestconfig,
+ isolated=isolated,
+ pid=top_level_pidns,
+ unshare_inline=args.unshare_inline if args else unshare_inline,
+ logger=logger,
+ )
+
+ try:
+ await unet._async_build(logger) # pylint: disable=W0212
+ except Exception as error:
+ logging.critical("Failure building munet topology: %s", error, exc_info=True)
+ await unet.async_delete()
+ raise
+ except KeyboardInterrupt:
+ await unet.async_delete()
+ raise
+
+ topoconf = config.get("topology")
+ if not topoconf:
+ return unet
+
+ dns_network = topoconf.get("dns-network")
+ if dns_network:
+ append_hosts_files(unet, dns_network)
+
+ # Write our current config to the run directory
+ with open(f"{unet.rundir}/config.json", "w", encoding="utf-8") as f:
+ json.dump(unet.config, f, indent=2)
+
+ return unet
+
+
+def build_topology(config=None, logger=None, rundir=None, args=None, pytestconfig=None):
+ return asyncio.run(async_build_topology(config, logger, rundir, args, pytestconfig))
diff --git a/tests/topotests/munet/testing/__init__.py b/tests/topotests/munet/testing/__init__.py
new file mode 100644
index 0000000..63cbfab
--- /dev/null
+++ b/tests/topotests/munet/testing/__init__.py
@@ -0,0 +1 @@
+"""Sub-package supporting munet use in pytest."""
diff --git a/tests/topotests/munet/testing/fixtures.py b/tests/topotests/munet/testing/fixtures.py
new file mode 100644
index 0000000..25039df
--- /dev/null
+++ b/tests/topotests/munet/testing/fixtures.py
@@ -0,0 +1,447 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# April 22 2022, Christian Hopps <chopps@gmail.com>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C
+#
+"""A module that implements pytest fixtures.
+
+To use in your project, in your conftest.py add:
+
+ from munet.testing.fixtures import *
+"""
+import contextlib
+import logging
+import os
+
+from pathlib import Path
+from typing import Union
+
+import pytest
+import pytest_asyncio
+
+from ..base import BaseMunet
+from ..base import Bridge
+from ..base import get_event_loop
+from ..cleanup import cleanup_current
+from ..cleanup import cleanup_previous
+from ..native import L3NodeMixin
+from ..parser import async_build_topology
+from ..parser import get_config
+from .util import async_pause_test
+from .util import pause_test
+
+
+@contextlib.asynccontextmanager
+async def achdir(ndir: Union[str, Path], desc=""):
+ odir = os.getcwd()
+ os.chdir(ndir)
+ if desc:
+ logging.debug("%s: chdir from %s to %s", desc, odir, ndir)
+ try:
+ yield
+ finally:
+ if desc:
+ logging.debug("%s: chdir back from %s to %s", desc, ndir, odir)
+ os.chdir(odir)
+
+
+@contextlib.contextmanager
+def chdir(ndir: Union[str, Path], desc=""):
+ odir = os.getcwd()
+ os.chdir(ndir)
+ if desc:
+ logging.debug("%s: chdir from %s to %s", desc, odir, ndir)
+ try:
+ yield
+ finally:
+ if desc:
+ logging.debug("%s: chdir back from %s to %s", desc, ndir, odir)
+ os.chdir(odir)
+
+
+def get_test_logdir(nodeid=None, module=False):
+ """Get log directory relative pathname."""
+ xdist_worker = os.getenv("PYTEST_XDIST_WORKER", "")
+ mode = os.getenv("PYTEST_XDIST_MODE", "no")
+
+ # nodeid: all_protocol_startup/test_all_protocol_startup.py::test_router_running
+ # may be missing "::testname" if module is True
+ if not nodeid:
+ nodeid = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0]
+
+ cur_test = nodeid.replace("[", "_").replace("]", "_")
+ if module:
+ idx = cur_test.rfind("::")
+ path = cur_test if idx == -1 else cur_test[:idx]
+ testname = ""
+ else:
+ path, testname = cur_test.split("::")
+ testname = testname.replace("/", ".")
+ path = path[:-3].replace("/", ".")
+
+ # We use different logdir paths based on how xdist is running.
+ if mode == "each":
+ if module:
+ return os.path.join(path, "worker-logs", xdist_worker)
+ return os.path.join(path, testname, xdist_worker)
+ assert mode in ("no", "load", "loadfile", "loadscope"), f"Unknown dist mode {mode}"
+ return path if module else os.path.join(path, testname)
+
+
+def _push_log_handler(desc, logpath):
+ logpath = os.path.abspath(logpath)
+ logging.debug("conftest: adding %s logging at %s", desc, logpath)
+ root_logger = logging.getLogger()
+ handler = logging.FileHandler(logpath, mode="w")
+ fmt = logging.Formatter("%(asctime)s %(levelname)5s: %(message)s")
+ handler.setFormatter(fmt)
+ root_logger.addHandler(handler)
+ return handler
+
+
+def _pop_log_handler(handler):
+ root_logger = logging.getLogger()
+ logging.debug("conftest: removing logging handler %s", handler)
+ root_logger.removeHandler(handler)
+
+
+@contextlib.contextmanager
+def log_handler(desc, logpath):
+ handler = _push_log_handler(desc, logpath)
+ try:
+ yield
+ finally:
+ _pop_log_handler(handler)
+
+
+# =================
+# Sessions Fixtures
+# =================
+
+
+@pytest.fixture(autouse=True, scope="session")
+def session_autouse():
+ 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
+
+ if not is_worker:
+ # This is unfriendly to multi-instance
+ cleanup_previous()
+
+ # We never pop as we want to keep logging
+ _push_log_handler("session", "/tmp/unet-test/pytest-session.log")
+
+ yield
+
+ if not is_worker:
+ cleanup_current()
+
+
+# ===============
+# Module Fixtures
+# ===============
+
+
+@pytest.fixture(autouse=True, scope="module")
+def module_autouse(request):
+ logpath = get_test_logdir(request.node.name, True)
+ logpath = os.path.join("/tmp/unet-test", logpath, "pytest-exec.log")
+ with log_handler("module", logpath):
+ sdir = os.path.dirname(os.path.realpath(request.fspath))
+ with chdir(sdir, "module autouse fixture"):
+ yield
+
+ if BaseMunet.g_unet:
+ raise Exception("Base Munet was not cleaned up/deleted")
+
+
+@pytest.fixture(scope="module")
+def event_loop():
+ """Create an instance of the default event loop for the session."""
+ loop = get_event_loop()
+ try:
+ logging.info("event_loop_fixture: yielding with new event loop watcher")
+ yield loop
+ finally:
+ loop.close()
+
+
+@pytest.fixture(scope="module")
+def rundir_module():
+ d = os.path.join("/tmp/unet-test", get_test_logdir(module=True))
+ logging.debug("conftest: test module rundir %s", d)
+ return d
+
+
+async def _unet_impl(
+ _rundir, _pytestconfig, unshare=None, top_level_pidns=None, param=None
+):
+ try:
+ # Default is not to unshare inline if not specified otherwise
+ unshare_default = False
+ pidns_default = True
+ if isinstance(param, (tuple, list)):
+ pidns_default = bool(param[2]) if len(param) > 2 else True
+ unshare_default = bool(param[1]) if len(param) > 1 else False
+ param = str(param[0])
+ elif isinstance(param, bool):
+ unshare_default = param
+ param = None
+ if unshare is None:
+ unshare = unshare_default
+ if top_level_pidns is None:
+ top_level_pidns = pidns_default
+
+ logging.info("unet fixture: basename=%s unshare_inline=%s", param, unshare)
+ _unet = await async_build_topology(
+ config=get_config(basename=param) if param else None,
+ rundir=_rundir,
+ unshare_inline=unshare,
+ top_level_pidns=top_level_pidns,
+ pytestconfig=_pytestconfig,
+ )
+ except Exception as error:
+ logging.debug(
+ "unet fixture: unet build failed: %s\nparam: %s",
+ error,
+ param,
+ exc_info=True,
+ )
+ pytest.skip(
+ f"unet fixture: unet build failed: {error}", allow_module_level=True
+ )
+ raise
+
+ try:
+ tasks = await _unet.run()
+ except Exception as error:
+ logging.debug("unet fixture: unet run failed: %s", error, exc_info=True)
+ await _unet.async_delete()
+ pytest.skip(f"unet fixture: unet run failed: {error}", allow_module_level=True)
+ raise
+
+ logging.debug("unet fixture: containers running")
+
+ # Pytest is supposed to always return even if exceptions
+ try:
+ yield _unet
+ except Exception as error:
+ logging.error("unet fixture: yield unet unexpected exception: %s", error)
+
+ logging.debug("unet fixture: module done, deleting unet")
+ await _unet.async_delete()
+
+ # No one ever awaits these so cancel them
+ logging.debug("unet fixture: cleanup")
+ for task in tasks:
+ task.cancel()
+
+ # Reset the class variables so auto number is predictable
+ logging.debug("unet fixture: resetting ords to 1")
+ L3NodeMixin.next_ord = 1
+ Bridge.next_ord = 1
+
+
+@pytest.fixture(scope="module")
+async def unet(request, rundir_module, pytestconfig): # pylint: disable=W0621
+ """A unet creating fixutre.
+
+ The request param is either the basename of the config file or a tuple of the form:
+ (basename, unshare, top_level_pidns), with the second and third elements boolean and
+ optional, defaulting to False, True.
+ """
+ param = request.param if hasattr(request, "param") else None
+ sdir = os.path.dirname(os.path.realpath(request.fspath))
+ async with achdir(sdir, "unet fixture"):
+ async for x in _unet_impl(rundir_module, pytestconfig, param=param):
+ yield x
+
+
+@pytest.fixture(scope="module")
+async def unet_share(request, rundir_module, pytestconfig): # pylint: disable=W0621
+ """A unet creating fixutre.
+
+ This share variant keeps munet from unsharing the process to a new namespace so that
+ root level commands and actions are execute on the host, normally they are executed
+ in the munet namespace which allowing things like scapy inline in tests to work.
+
+ The request param is either the basename of the config file or a tuple of the form:
+ (basename, top_level_pidns), the second value is a boolean.
+ """
+ param = request.param if hasattr(request, "param") else None
+ if isinstance(param, (tuple, list)):
+ param = (param[0], False, param[1])
+ sdir = os.path.dirname(os.path.realpath(request.fspath))
+ async with achdir(sdir, "unet_share fixture"):
+ async for x in _unet_impl(
+ rundir_module, pytestconfig, unshare=False, param=param
+ ):
+ yield x
+
+
+@pytest.fixture(scope="module")
+async def unet_unshare(request, rundir_module, pytestconfig): # pylint: disable=W0621
+ """A unet creating fixutre.
+
+ This unshare variant has the top level munet unshare the process inline so that
+ root level commands and actions are execute in a new namespace. This allows things
+ like scapy inline in tests to work.
+
+ The request param is either the basename of the config file or a tuple of the form:
+ (basename, top_level_pidns), the second value is a boolean.
+ """
+ param = request.param if hasattr(request, "param") else None
+ if isinstance(param, (tuple, list)):
+ param = (param[0], True, param[1])
+ sdir = os.path.dirname(os.path.realpath(request.fspath))
+ async with achdir(sdir, "unet_unshare fixture"):
+ async for x in _unet_impl(
+ rundir_module, pytestconfig, unshare=True, param=param
+ ):
+ yield x
+
+
+# =================
+# Function Fixtures
+# =================
+
+
+@pytest.fixture(autouse=True, scope="function")
+async def function_autouse(request):
+ async with achdir(
+ os.path.dirname(os.path.realpath(request.fspath)), "func.fixture"
+ ):
+ yield
+
+
+@pytest.fixture(autouse=True)
+async def check_for_pause(request, pytestconfig):
+ # When we unshare inline we can't pause in the pytest_runtest_makereport hook
+ # so do it here.
+ if BaseMunet.g_unet and BaseMunet.g_unet.unshare_inline:
+ pause = bool(pytestconfig.getoption("--pause"))
+ if pause:
+ await async_pause_test(f"XXX before test '{request.node.name}'")
+ yield
+
+
+@pytest.fixture(scope="function")
+def stepf(pytestconfig):
+ class Stepnum:
+ """Track the stepnum in closure."""
+
+ num = 0
+
+ def inc(self):
+ self.num += 1
+
+ pause = pytestconfig.getoption("pause")
+ stepnum = Stepnum()
+
+ def stepfunction(desc=""):
+ desc = f": {desc}" if desc else ""
+ if pause:
+ pause_test(f"before step {stepnum.num}{desc}")
+ logging.info("STEP %s%s", stepnum.num, desc)
+ stepnum.inc()
+
+ return stepfunction
+
+
+@pytest_asyncio.fixture(scope="function")
+async def astepf(pytestconfig):
+ class Stepnum:
+ """Track the stepnum in closure."""
+
+ num = 0
+
+ def inc(self):
+ self.num += 1
+
+ pause = pytestconfig.getoption("pause")
+ stepnum = Stepnum()
+
+ async def stepfunction(desc=""):
+ desc = f": {desc}" if desc else ""
+ if pause:
+ await async_pause_test(f"before step {stepnum.num}{desc}")
+ logging.info("STEP %s%s", stepnum.num, desc)
+ stepnum.inc()
+
+ return stepfunction
+
+
+@pytest.fixture(scope="function")
+def rundir():
+ d = os.path.join("/tmp/unet-test", get_test_logdir(module=False))
+ logging.debug("conftest: test function rundir %s", d)
+ return d
+
+
+# Configure logging
+@pytest.hookimpl(hookwrapper=True, tryfirst=True)
+def pytest_runtest_setup(item):
+ d = os.path.join(
+ "/tmp/unet-test", get_test_logdir(nodeid=item.nodeid, module=False)
+ )
+ config = item.config
+ logging_plugin = config.pluginmanager.get_plugin("logging-plugin")
+ filename = Path(d, "pytest-exec.log")
+ logging_plugin.set_log_path(str(filename))
+ logging.debug("conftest: test function setup: rundir %s", d)
+ yield
+
+
+@pytest.fixture
+async def unet_perfunc(request, rundir, pytestconfig): # pylint: disable=W0621
+ param = request.param if hasattr(request, "param") else None
+ async for x in _unet_impl(rundir, pytestconfig, param=param):
+ yield x
+
+
+@pytest.fixture
+async def unet_perfunc_unshare(request, rundir, pytestconfig): # pylint: disable=W0621
+ """Build unet per test function with an optional topology basename parameter.
+
+ The fixture can be parameterized to choose different config files.
+ For example, use as follows to run the test with unet_perfunc configured
+ first with a config file named `cfg1.yaml` then with config file `cfg2.yaml`
+ (where the actual files could end with `json` or `toml` rather than `yaml`).
+
+ @pytest.mark.parametrize(
+ "unet_perfunc", ["cfg1", "cfg2]", indirect=["unet_perfunc"]
+ )
+ def test_example(unet_perfunc)
+ """
+ param = request.param if hasattr(request, "param") else None
+ async for x in _unet_impl(rundir, pytestconfig, unshare=True, param=param):
+ yield x
+
+
+@pytest.fixture
+async def unet_perfunc_share(request, rundir, pytestconfig): # pylint: disable=W0621
+ """Build unet per test function with an optional topology basename parameter.
+
+ This share variant keeps munet from unsharing the process to a new namespace so that
+ root level commands and actions are execute on the host, normally they are executed
+ in the munet namespace which allowing things like scapy inline in tests to work.
+
+ The fixture can be parameterized to choose different config files. For example, use
+ as follows to run the test with unet_perfunc configured first with a config file
+ named `cfg1.yaml` then with config file `cfg2.yaml` (where the actual files could
+ end with `json` or `toml` rather than `yaml`).
+
+ @pytest.mark.parametrize(
+ "unet_perfunc", ["cfg1", "cfg2]", indirect=["unet_perfunc"]
+ )
+ def test_example(unet_perfunc)
+ """
+ param = request.param if hasattr(request, "param") else None
+ async for x in _unet_impl(rundir, pytestconfig, unshare=False, param=param):
+ yield x
diff --git a/tests/topotests/munet/testing/hooks.py b/tests/topotests/munet/testing/hooks.py
new file mode 100644
index 0000000..9b6a49a
--- /dev/null
+++ b/tests/topotests/munet/testing/hooks.py
@@ -0,0 +1,225 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# April 22 2022, Christian Hopps <chopps@gmail.com>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C
+#
+"""A module that implements pytest hooks.
+
+To use in your project, in your conftest.py add:
+
+ from munet.testing.hooks import *
+"""
+import logging
+import os
+import sys
+import traceback
+
+import pytest
+
+from ..base import BaseMunet # pylint: disable=import-error
+from ..cli import cli # pylint: disable=import-error
+from .util import pause_test
+
+
+# ===================
+# Hooks (non-fixture)
+# ===================
+
+
+def pytest_addoption(parser):
+ parser.addoption(
+ "--cli-on-error",
+ action="store_true",
+ help="CLI on test failure",
+ )
+
+ parser.addoption(
+ "--coverage",
+ action="store_true",
+ help="Enable coverage gathering if supported",
+ )
+
+ parser.addoption(
+ "--gdb",
+ default="",
+ metavar="HOST[,HOST...]",
+ help="Comma-separated list of nodes to launch gdb on, or 'all'",
+ )
+ parser.addoption(
+ "--gdb-breakpoints",
+ default="",
+ metavar="BREAKPOINT[,BREAKPOINT...]",
+ help="Comma-separated list of breakpoints",
+ )
+ parser.addoption(
+ "--gdb-use-emacs",
+ action="store_true",
+ help="Use emacsclient to run gdb instead of a shell",
+ )
+
+ parser.addoption(
+ "--pcap",
+ default="",
+ metavar="NET[,NET...]",
+ help="Comma-separated list of networks to capture packets on, or 'all'",
+ )
+
+ parser.addoption(
+ "--pause",
+ action="store_true",
+ help="Pause after each test",
+ )
+ parser.addoption(
+ "--pause-at-end",
+ action="store_true",
+ help="Pause before taking munet down",
+ )
+ parser.addoption(
+ "--pause-on-error",
+ action="store_true",
+ help="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)",
+ )
+
+ parser.addoption(
+ "--shell",
+ default="",
+ metavar="NODE[,NODE...]",
+ help="Comma-separated list of nodes to spawn shell on, or 'all'",
+ )
+
+ parser.addoption(
+ "--stdout",
+ default="",
+ metavar="NODE[,NODE...]",
+ help="Comma-separated list of nodes to open tail-f stdout window on, or 'all'",
+ )
+
+ parser.addoption(
+ "--stderr",
+ default="",
+ metavar="NODE[,NODE...]",
+ help="Comma-separated list of nodes to open tail-f stderr window on, or 'all'",
+ )
+
+
+def pytest_configure(config):
+ if "PYTEST_XDIST_WORKER" not in os.environ:
+ os.environ["PYTEST_XDIST_MODE"] = config.getoption("dist", "no")
+ os.environ["PYTEST_IS_WORKER"] = ""
+ is_xdist = os.environ["PYTEST_XDIST_MODE"] != "no"
+ is_worker = False
+ else:
+ os.environ["PYTEST_IS_WORKER"] = os.environ["PYTEST_XDIST_WORKER"]
+ is_xdist = True
+ is_worker = True
+
+ # 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
+
+ for winopt in ["--shell", "--stdout", "--stderr"]:
+ b = config.getoption(winopt)
+ if b and xdist_no_windows:
+ pytest.exit(
+ f"{winopt} use requires byobu/TMUX/XTerm "
+ f"under dist {os.environ['PYTEST_XDIST_MODE']}"
+ )
+ elif b and not is_xdist and not have_windows:
+ pytest.exit(f"{winopt} use requires byobu/TMUX/SCREEN/XTerm")
+
+
+def pytest_runtest_makereport(item, call):
+ """Pause or invoke CLI as directed by config."""
+ isatty = sys.stdout.isatty()
+
+ pause = bool(item.config.getoption("--pause"))
+ skipped = False
+
+ if call.excinfo is None:
+ error = False
+ elif call.excinfo.typename == "Skipped":
+ skipped = True
+ error = False
+ pause = False
+ else:
+ error = True
+ modname = item.parent.module.__name__
+ exval = call.excinfo.value
+ logging.error(
+ "test %s/%s failed: %s: stdout: '%s' stderr: '%s'",
+ modname,
+ item.name,
+ exval,
+ exval.stdout if hasattr(exval, "stdout") else "NA",
+ exval.stderr if hasattr(exval, "stderr") else "NA",
+ )
+ if not pause:
+ pause = item.config.getoption("--pause-on-error")
+
+ if error and isatty and item.config.getoption("--cli-on-error"):
+ if not BaseMunet.g_unet:
+ logging.error("Could not launch CLI b/c no munet exists yet")
+ else:
+ print(f"\nCLI-ON-ERROR: {call.excinfo.typename}")
+ print(f"CLI-ON-ERROR:\ntest {modname}/{item.name} failed: {exval}")
+ if hasattr(exval, "stdout") and exval.stdout:
+ print("stdout: " + exval.stdout.replace("\n", "\nstdout: "))
+ if hasattr(exval, "stderr") and exval.stderr:
+ print("stderr: " + exval.stderr.replace("\n", "\nstderr: "))
+ cli(BaseMunet.g_unet)
+
+ if pause:
+ if skipped:
+ item.skip_more_pause = True
+ elif hasattr(item, "skip_more_pause"):
+ pass
+ elif call.when == "setup":
+ if error:
+ item.skip_more_pause = True
+
+ # we can't asyncio.run() (which pause does) if we are unhsare_inline
+ # at this point, count on an autouse fixture to pause instead in this
+ # case
+ if not BaseMunet.g_unet or not BaseMunet.g_unet.unshare_inline:
+ pause_test(f"before test '{item.nodeid}'")
+
+ # check for a result to try and catch setup (or module setup) failure
+ # e.g., after a module level fixture fails, we do not want to pause on every
+ # skipped test.
+ elif call.when == "teardown" and call.excinfo:
+ logging.warning(
+ "Caught exception during teardown: %s\n:Traceback:\n%s",
+ call.excinfo,
+ "".join(traceback.format_tb(call.excinfo.tb)),
+ )
+ pause_test(f"after teardown after test '{item.nodeid}'")
+ elif call.when == "teardown" and call.result:
+ pause_test(f"after test '{item.nodeid}'")
+ elif error:
+ item.skip_more_pause = True
+ print(f"\nPAUSE-ON-ERROR: {call.excinfo.typename}")
+ print(f"PAUSE-ON-ERROR:\ntest {modname}/{item.name} failed: {exval}")
+ if hasattr(exval, "stdout") and exval.stdout:
+ print("stdout: " + exval.stdout.replace("\n", "\nstdout: "))
+ if hasattr(exval, "stderr") and exval.stderr:
+ print("stderr: " + exval.stderr.replace("\n", "\nstderr: "))
+ pause_test(f"PAUSE-ON-ERROR: '{item.nodeid}'")
diff --git a/tests/topotests/munet/testing/util.py b/tests/topotests/munet/testing/util.py
new file mode 100644
index 0000000..a1a94bc
--- /dev/null
+++ b/tests/topotests/munet/testing/util.py
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# April 22 2022, Christian Hopps <chopps@gmail.com>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C
+#
+"""Utility functions useful when using munet testing functionailty in pytest."""
+import asyncio
+import datetime
+import functools
+import logging
+import sys
+import time
+
+from ..base import BaseMunet
+from ..cli import async_cli
+
+
+# =================
+# Utility Functions
+# =================
+
+
+async def async_pause_test(desc=""):
+ isatty = sys.stdout.isatty()
+ if not isatty:
+ desc = f" for {desc}" if desc else ""
+ logging.info("NO PAUSE on non-tty terminal%s", desc)
+ return
+
+ while True:
+ if desc:
+ print(f"\n== PAUSING: {desc} ==")
+ try:
+ user = input('PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: ')
+ except EOFError:
+ print("^D...continuing")
+ break
+ user = user.strip()
+ if user == "cli":
+ await async_cli(BaseMunet.g_unet)
+ elif user == "pdb":
+ breakpoint() # pylint: disable=W1515
+ elif user:
+ print(f'Unrecognized input: "{user}"')
+ else:
+ break
+
+
+def pause_test(desc=""):
+ asyncio.run(async_pause_test(desc))
+
+
+def retry(retry_timeout, initial_wait=0, expected=True):
+ """decorator: retry while functions return is not None or 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 non None ends the retry loop, and returns that value)
+ """
+
+ def _retry(func):
+ @functools.wraps(func)
+ def func_retry(*args, **kwargs):
+ 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)
+ retry_until = datetime.datetime.now() + datetime.timedelta(
+ seconds=_retry_timeout + _initial_wait
+ )
+
+ if initial_wait > 0:
+ logging.info("Waiting for [%s]s as initial delay", initial_wait)
+ time.sleep(initial_wait)
+
+ while True:
+ seconds_left = (retry_until - datetime.datetime.now()).total_seconds()
+ try:
+ ret = func(*args, **kwargs)
+ if _expected and ret is None:
+ logging.debug("Function succeeds")
+ return ret
+ logging.debug("Function returned %s", ret)
+ except Exception as error:
+ logging.info("Function raised exception: %s", str(error))
+ ret = error
+
+ if seconds_left < 0:
+ logging.info("Retry timeout of %ds reached", _retry_timeout)
+ if isinstance(ret, Exception):
+ raise ret
+ return ret
+
+ logging.info(
+ "Sleeping %ds until next retry with %.1f retry time left",
+ retry_sleep,
+ seconds_left,
+ )
+ time.sleep(retry_sleep)
+
+ func_retry._original = func # pylint: disable=W0212
+ return func_retry
+
+ return _retry
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/sharp_route4.json b/tests/topotests/nhrp_topo/r1/sharp_route4.json
new file mode 100644
index 0000000..4c4b8ea
--- /dev/null
+++ b/tests/topotests/nhrp_topo/r1/sharp_route4.json
@@ -0,0 +1,46 @@
+{
+ "4.4.4.1\/32":[
+ {
+ "prefix":"4.4.4.1\/32",
+ "prefixLen":32,
+ "protocol":"sharp",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.255.255.2",
+ "interfaceName":"r1-gre0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.1\/32":[
+ {
+ "prefix":"5.5.5.1\/32",
+ "prefixLen":32,
+ "protocol":"sharp",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.255.255.2",
+ "interfaceName":"r1-gre0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
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..78b82ed
--- /dev/null
+++ b/tests/topotests/nhrp_topo/test_nhrp_topo.py
@@ -0,0 +1,253 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_nhrp_topo.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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))
+ )
+
+ # Include sharpd for r1
+ if rname == "r1":
+ router.load_config(
+ TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.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_route_install():
+ "Test use of NHRP routes by other protocols (sharpd here)."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Testing route install over NHRP tunnel")
+
+ # Install sharpd routes over an NHRP route
+ r1 = tgen.gears["r1"]
+
+ # Install one recursive and one non-recursive sharpd route
+ r1.vtysh_cmd("sharp install route 4.4.4.1 nexthop 10.255.255.2 1")
+
+ r1.vtysh_cmd("sharp install route 5.5.5.1 nexthop 10.255.255.2 1 no-recurse")
+
+ json_file = "{}/{}/sharp_route4.json".format(CWD, "r1")
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route sharp json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
+
+ logger.info("Sharp routes:")
+ output = r1.vtysh_cmd("show ip route sharp")
+ logger.info(output)
+
+ assertmsg = '"{}" JSON route output mismatches'.format(r1.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/ospf6_ecmp_inter_area/r1/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf
new file mode 100644
index 0000000..e6e5733
--- /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..ec15ff9
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py
@@ -0,0 +1,194 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# test_ospf6_ecmp_inter_area.py
+#
+# Copyright (c) 2021, 2022 by Martin Buck
+# Copyright (c) 2016 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+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..aa9438b
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf
@@ -0,0 +1,29 @@
+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..181d376
--- /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":0,
+ "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":10,
+ "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":20,
+ "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":30,
+ "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":40,
+ "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":30,
+ "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":40,
+ "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..13b5cd4
--- /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":10,
+ "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":0,
+ "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":10,
+ "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":20,
+ "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":30,
+ "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":20,
+ "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":30,
+ "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..db6ec3e
--- /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":20,
+ "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":10,
+ "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":0,
+ "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":10,
+ "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":20,
+ "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":10,
+ "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":20,
+ "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..08ccff2
--- /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":30,
+ "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":20,
+ "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":10,
+ "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":0,
+ "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":10,
+ "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":20,
+ "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":30,
+ "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..8ddd55b
--- /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":40,
+ "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":30,
+ "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":20,
+ "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":10,
+ "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":0,
+ "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":30,
+ "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":40,
+ "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..9d45b09
--- /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":30,
+ "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":20,
+ "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":10,
+ "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":20,
+ "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":30,
+ "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":0,
+ "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":10,
+ "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..c4f8414
--- /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":40,
+ "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":30,
+ "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":20,
+ "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":30,
+ "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":40,
+ "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":10,
+ "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":0,
+ "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..45e1bc8
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py
@@ -0,0 +1,583 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf6_gr_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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 = 10
+ 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 = 10
+ 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")
+
+
+#
+# Test rt1 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt1():
+ logger.info("Test: verify rt1 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt1", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt1", ["ospf6d"])
+
+ expect_grace_lsa(restarting="1.1.1.1", helper="rt2")
+ ensure_gr_is_in_zebra("rt1")
+ check_routers(restarting="rt1")
+
+
+#
+# Test rt2 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt2():
+ logger.info("Test: verify rt2 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt2", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt2", ["ospf6d"])
+
+ 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")
+ check_routers(restarting="rt2")
+
+
+#
+# Test rt3 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt3():
+ logger.info("Test: verify rt3 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt3", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt3", ["ospf6d"])
+
+ 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")
+ check_routers(restarting="rt3")
+
+
+#
+# Test rt4 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt4():
+ logger.info("Test: verify rt4 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt4", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt4", ["ospf6d"])
+
+ 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")
+ check_routers(restarting="rt4")
+
+
+#
+# Test rt5 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt5():
+ logger.info("Test: verify rt5 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt5", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt5", ["ospf6d"])
+
+ expect_grace_lsa(restarting="5.5.5.5", helper="rt4")
+ ensure_gr_is_in_zebra("rt5")
+ check_routers(restarting="rt5")
+
+
+#
+# Test rt6 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt6():
+ logger.info("Test: verify rt6 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt6", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt6", ["ospf6d"])
+
+ 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")
+ check_routers(restarting="rt6")
+
+
+#
+# Test rt7 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt7():
+ logger.info("Test: verify rt7 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt7", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt7", ["ospf6d"])
+
+ expect_grace_lsa(restarting="6.6.6.6", helper="rt6")
+ ensure_gr_is_in_zebra("rt7")
+ 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_loopback_cost/__init__.py b/tests/topotests/ospf6_loopback_cost/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ospf6_loopback_cost/__init__.py
diff --git a/tests/topotests/ospf6_loopback_cost/r1/frr.conf b/tests/topotests/ospf6_loopback_cost/r1/frr.conf
new file mode 100644
index 0000000..d85166b
--- /dev/null
+++ b/tests/topotests/ospf6_loopback_cost/r1/frr.conf
@@ -0,0 +1,16 @@
+!
+int lo
+ ipv6 address 2001:db8::1/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 passive
+!
+int r1-eth0
+ ipv6 address 2001:db8:1::1/64
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 dead-interval 4
+!
+router ospf6
+ ospf6 router-id 0.0.0.1
+exit
+!
diff --git a/tests/topotests/ospf6_loopback_cost/r2/frr.conf b/tests/topotests/ospf6_loopback_cost/r2/frr.conf
new file mode 100644
index 0000000..8f3e2ca
--- /dev/null
+++ b/tests/topotests/ospf6_loopback_cost/r2/frr.conf
@@ -0,0 +1,16 @@
+!
+int lo
+ ipv6 address 2001:db8::2/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 passive
+!
+int r2-eth0
+ ipv6 address 2001:db8:1::2/64
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 dead-interval 4
+!
+router ospf6
+ ospf6 router-id 0.0.0.2
+exit
+!
diff --git a/tests/topotests/ospf6_loopback_cost/test_ospf6_loopback_cost.py b/tests/topotests/ospf6_loopback_cost/test_ospf6_loopback_cost.py
new file mode 100644
index 0000000..8e7a7ea
--- /dev/null
+++ b/tests/topotests/ospf6_loopback_cost/test_ospf6_loopback_cost.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if OSPFv3 loopback interfaces get a cost of 0.
+
+https://www.rfc-editor.org/rfc/rfc5340.html#page-37:
+
+If the interface type is point-to-multipoint or the interface is
+in the state Loopback, the global scope IPv6 addresses associated
+with the interface (if any) are copied into the intra-area-prefix-LSA
+with the PrefixOptions LA-bit set, the PrefixLength set to 128, and
+the metric set to 0.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = pytest.mark.ospf6d
+
+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
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for _, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_ospf6_loopback_cost():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ def _show_ipv6_route():
+ output = json.loads(r1.vtysh_cmd("show ipv6 route json"))
+ expected = {
+ "2001:db8::1/128": [
+ {
+ "metric": 0,
+ "distance": 110,
+ }
+ ],
+ "2001:db8::2/128": [
+ {
+ "metric": 10,
+ "distance": 110,
+ }
+ ],
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _show_ipv6_route,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Loopback cost isn't 0"
+
+
+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..5649757
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/test_ospf6_topo1.py
@@ -0,0 +1,422 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf6_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2016 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+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..f982990
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py
@@ -0,0 +1,462 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# 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.
+#
+
+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..f95f7bb
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py
@@ -0,0 +1,653 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf6_topo2.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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=5, 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_flood_reduction.json b/tests/topotests/ospf_basic_functionality/ospf_flood_reduction.json
new file mode 100644
index 0000000..74443cd
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_flood_reduction.json
@@ -0,0 +1,214 @@
+{
+
+ "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": {}
+ },
+ "graceful-restart": {
+ "helper-only": []
+ }
+ }
+ },
+ "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.1",
+ "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": {}
+ },
+ "graceful-restart": {
+ "helper-only": []
+ }
+ }
+ },
+ "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.1",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "broadcast"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ },
+ "graceful-restart": {
+ "helper-only": []
+ }
+ }
+ },
+ "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.2",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "broadcast"
+ }
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ },
+ "graceful-restart": {
+ "helper-only": []
+ }
+ }
+ }
+ }
+} \ 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..5486338
--- /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": 10,
+ "priority": 98
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 10,
+ "priority": 99
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 10,
+ "priority": 0
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 10,
+ "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": {}
+ }
+ }
+ }
+ }
+}
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..1fe076e
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_rte_calc.json
@@ -0,0 +1,168 @@
+{
+ "feature": ["bgp"],
+ "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..0531e81
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
@@ -0,0 +1,3276 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+ 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.
+
+ # 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 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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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": {
+ "summaryAddress": "11.0.0.0/16",
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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": {
+ "summaryAddress": "11.0.0.0/24",
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB \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 \n Expected: Routes should not be present in RIB\n"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB \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 \n Expected: Routes should not be present in RIB"
+ "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 Expected: Routes should not be present in OSPF RIB \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 \n Expected: Summary Route should not present in RIB"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Summary Route should not present in OSPF DB"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB \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 \n Expected: Routes should not be present in RIB\n"
+ "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 Expected: Routes should not be present in OSPF RIB \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 \n Expected: Routes should not be present in RIB"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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("{}".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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB. \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 \n Expected: Routes should not be present in RIB"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 1234,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB \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 \n Expected: Summary Routes should not be present in RIB. \n"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 1234,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 1,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 4294967295,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB \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 \n Expected: Routes should not be present in RIB\n"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 88888,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB \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 \n Expected: Routes should not be present in RIB\n"
+ "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 to R1 from 0.0.0.0 to 0.0.0.1")
+ 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 on R1 to R0 from 0.0.0.0 to 0.0.0.1")
+ 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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 1234,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB \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 \n Expected: Routes should not be present in RIB "
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 1234,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 1,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 4294967295,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB \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\n Expected: Routes should not be present in RIB.\n"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 88888,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB \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 \n Expected: Routes should not be present in RIB \n"
+ "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 Expected: Routes should not be present in OSPF RIB.\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\n Expected: Routes should not be present in RIB."
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB. \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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 1234,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB. \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 \n Expected: Routes should not be present in RIB \n"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB \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\n Expected: Routes should not be present in RIB"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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 \n Expected: Routes should not be present in OSPF RIB.\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\n Expected: Routes should not be present in RIB.\n"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB\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 \n Expected: Routes should not be present in RIB.\n"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB.\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\n Expected: Routes should not be present in RIB"
+ "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": {
+ "summaryAddress": "12.0.0.0/8",
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv4"][2],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB.\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\n Expected: Routes should not be present in RIB.\n"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB. \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\n Expected: Routes should not be present in RIB\n"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB. \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\n Expected: Routes should not be present in RIB\n"
+ "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]: {
+ "summaryAddress": SUMMARY["ipv4"][0],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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 Expected: Routes should not be present in OSPF RIB. \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\n Expected: Routes should not be present in RIB.\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/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..603aead
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py
@@ -0,0 +1,388 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+)
+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.
+
+ # 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 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]: {
+ "summaryAddress": SUMMARY["ipv4"][3],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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": {
+ "summaryAddress": "13.0.0.0/8",
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv4"][3],
+ "metricType": "E2",
+ "metric": 20,
+ "tag": 0,
+ "externalRouteCount": 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..8dd1030
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py
@@ -0,0 +1,1333 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+)
+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 MD5 keychain authentication.
+4. Verify ospf authentication with SHA256 keychain authentication.
+5. 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.
+
+ # 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)
+
+ 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_authentication_md5_keychain_tc30_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 key-chain cmd."
+ )
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm md5"""
+ )
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ 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, "Testcase 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."
+ )
+
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm md5"""
+ )
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ 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, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "Disable message-digest authentication on R2 using no ip ospf "
+ "authentication key-chain cmd."
+ )
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ "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, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step("Again On R2 enable ospf on interface with key-chain auth")
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ 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, "Testcase 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, "Testcase 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, "Testcase 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")
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm md5"""
+ )
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ 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, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_authentication_sha256_keychain_tc32_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 key-chain cmd."
+ )
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm hmac-sha-256"""
+ )
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ 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, "Testcase 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."
+ )
+
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm hmac-sha-256"""
+ )
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ 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, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "Disable message-digest authentication on R2 using no ip ospf "
+ "authentication key-chain cmd."
+ )
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ "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, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step("Again On R2 enable ospf on interface with key-chain auth")
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ 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, "Testcase 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, "Testcase 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, "Testcase 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")
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm hmac-sha-256"""
+ )
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ 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, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_authentication_different_auths_tc35_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."
+ )
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
+ 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step("Enable SHA-256 authentication on the interface")
+
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm hmac-sha-256"""
+ )
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm hmac-sha-256"""
+ )
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ 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, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step("Change the SHA-256 authentication password")
+
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string OSPFv4
+ cryptographic-algorithm hmac-sha-512"""
+ )
+
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string OSPFv4
+ cryptographic-algorithm hmac-sha-512"""
+ )
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase 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/ospf_basic_functionality/test_ospf_chaos.py b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py
new file mode 100644
index 0000000..e58f081
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py
@@ -0,0 +1,513 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+ 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.
+
+ # 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)
+
+ 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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..aba313d
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py
@@ -0,0 +1,440 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+)
+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.
+
+ # 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 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, "Testcase 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, "Testcase 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, "Testcase 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..1eeb23e
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py
@@ -0,0 +1,290 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+)
+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.
+
+ # 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 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, "Testcase 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, "Testcase 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, "Testcase 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_flood_reduction.py b/tests/topotests/ospf_basic_functionality/test_ospf_flood_reduction.py
new file mode 100644
index 0000000..bd7db64
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_flood_reduction.py
@@ -0,0 +1,1066 @@
+#!/usr/bin/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.
+#
+
+
+"""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/"))
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+# Global variables
+topo = None
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+# 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_static_routes,
+ step,
+ topo_daemons,
+ shutdown_bringup_interface,
+ check_router_status,
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ create_static_routes,
+ step,
+ shutdown_bringup_interface,
+ check_router_status,
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ stop_router,
+ start_router,
+ step,
+ create_static_routes,
+ kill_router_daemons,
+ check_router_status,
+ start_router_daemons,
+)
+from lib.topolog import logger
+from lib.topogen import Topogen, get_topogen
+
+from lib.topojson import build_config_from_json
+from lib.ospf import (
+ verify_ospf_neighbor,
+ clear_ospf,
+ create_router_ospf,
+ verify_ospf_database,
+ get_ospf_database,
+)
+
+# 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. Verify OSPF Flood reduction functionality with ospf enabled on process level.
+2. Verify OSPF Flood reduction functionality with ospf enabled on area level.
+3. Verify OSPF Flood reduction functionality between different area's.
+4. Verify OSPF Flood reduction functionality with ospf enabled on process level with default lsa refresh timer.
+5. Chaos - Verify OSPF TTL GTSM and flood reduction functionality in chaos scenarios.
+"""
+
+
+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 = "{}/ospf_flood_reduction.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)
+
+ # 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 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", "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_flood_red_tc1_p0(request):
+ """Verify OSPF Flood reduction functionality with ospf enabled on process 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():
+ check_router_status(tgen)
+
+ global topo
+
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+ red_static("r0")
+ step("Base config should be up, verify using OSPF convergence on all the routers")
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ 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("Enable flood reduction in process level on R0")
+ ospf_flood = {"r0": {"ospf": {"flood-reduction": True}}}
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step("Verify that ospf lsa's are set with dc bit 1.")
+ dut = "r0"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsaId": "100.1.1.0",
+ "options": "*|-|DC|-|-|-|E|-",
+ },
+ ]
+ }
+ },
+ }
+ result = verify_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure the custom refresh timer")
+ ospf_flood = {"r0": {"ospf": {"lsa-refresh": 120}}}
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step("Enable flood. reduction in all the routers of the topology.")
+ for rtr in topo["routers"].keys():
+ ospf_flood = {rtr: {"ospf": {"lsa-refresh": 120, "flood-reduction": True}}}
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that ospf lsa's are set with dc bit 1.")
+ for rtr in topo["routers"]:
+ dut = rtr
+ lsid = "{}".format(topo["routers"][rtr]["ospf"]["router_id"])
+ input_dict_db = {
+ "routerId": lsid,
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsaId": lsid,
+ "options": "*|-|DC|-|-|-|E|-",
+ },
+ ]
+ }
+ },
+ }
+ result = verify_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid=lsid
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Wait for 120 secs and verify that LSA's are not refreshed. ")
+ # get LSA age
+ dut = "r1"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {"lsaId": "100.1.1.0", "lsaAge": "get"},
+ ]
+ }
+ },
+ }
+ sleep(10)
+
+ result1 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+ # this wait is put so that we wait for 5secs to check if lsa is refreshed.
+ sleep(5)
+ result2 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+
+ assert (result1 == result2) is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Disable flood reduction in R0.")
+ ospf_flood = {
+ "r0": {"ospf": {"flood-reduction": True, "del_flood_reduction": True}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ clear_ospf(tgen, "r0")
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that ospf lea's are not set with dc bit 1.")
+ dut = "r0"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsaId": "100.1.1.0",
+ "options": "*|-|DC|-|-|-|E|-",
+ },
+ ]
+ }
+ },
+ }
+ result = verify_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0", expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Expected: OSPF LSA should not be set with DC bit in {} \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+ step("Wait for 120 secs and verify that LSA's are not refreshed. ")
+ # get LSA age
+ dut = "r1"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {"lsaId": "100.1.1.0", "lsaAge": "get"},
+ ]
+ }
+ },
+ }
+ sleep(10)
+
+ result1 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+ sleep(5)
+ result2 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+
+ if result2 is not result1:
+ result = True
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Enable flood reduction on each router with 10 secs delay of between each router."
+ )
+ for rtr in topo["routers"].keys():
+ ospf_flood = {rtr: {"ospf": {"lsa-refresh": 120, "flood-reduction": True}}}
+ sleep(10)
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr)
+
+ step("Verify that LSA's are not refreshed. Do not age bit should be set to 1.")
+ dut = "r0"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsaId": "100.1.1.0",
+ "options": "*|-|DC|-|-|-|E|-",
+ },
+ ]
+ }
+ },
+ }
+ result = verify_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify OSPF neighborship when OSPF flood reduction is configured and ospf process is restarted"
+ )
+
+ step("Kill OSPFd daemon on R0.")
+ kill_router_daemons(tgen, "r0", ["ospfd"])
+ start_router_daemons(tgen, "r0", ["ospfd"])
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that LSA's are not refreshed. Do not age bit should be set to 1.")
+ dut = "r0"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsaId": "100.1.1.0",
+ "options": "*|-|DC|-|-|-|E|-",
+ },
+ ]
+ }
+ },
+ }
+ result = verify_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_flood_red_tc2_p0(request):
+ """Verify OSPF Flood reduction functionality with ospf enabled on area 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():
+ check_router_status(tgen)
+
+ global topo
+
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+ red_static("r0")
+ step("Base config should be up, verify using OSPF convergence on all the routers")
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ 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("Enable flood reduction in area level on R0 in area 0")
+ ospf_flood = {
+ "r0": {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step("Verify that ospf lsa's are set with dc bit 1.")
+ dut = "r0"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsaId": "100.1.1.0",
+ "options": "*|-|DC|-|-|-|E|-",
+ },
+ ]
+ }
+ },
+ }
+ result = verify_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure the custom refresh timer")
+ ospf_flood = {"r0": {"ospf": {"lsa-refresh": 120}}}
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step("Enable flood. reduction in all the routers of the topology.")
+ for rtr in topo["routers"].keys():
+ ospf_flood = {
+ rtr: {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that ospf lsa's are set with dc bit 1.")
+ for rtr in topo["routers"]:
+ dut = rtr
+ lsid = "{}".format(topo["routers"][rtr]["ospf"]["router_id"])
+ input_dict_db = {
+ "routerId": lsid,
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsaId": lsid,
+ "options": "*|-|DC|-|-|-|E|-",
+ },
+ ]
+ }
+ },
+ }
+ result = verify_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid=lsid
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Wait for 120 secs and verify that LSA's are not refreshed. ")
+ # get LSA age
+ dut = "r1"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {"lsaId": "100.1.1.0", "lsaAge": "get"},
+ ]
+ }
+ },
+ }
+ sleep(10)
+
+ result1 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+ sleep(5)
+ result2 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+
+ assert (result1 == result2) is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Disable flood reduction in R0.")
+ ospf_flood = {
+ "r0": {
+ "ospf": {
+ "area": [{"id": "0.0.0.0", "flood-reduction": True, "delete": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ clear_ospf(tgen, "r0")
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that ospf lea's are not set with dc bit 1.")
+ dut = "r0"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsaId": "100.1.1.0",
+ "options": "*|-|DC|-|-|-|E|-",
+ },
+ ]
+ }
+ },
+ }
+ result = verify_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0", expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Expected: OSPF LSA should not be set with DC bit in {} \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+ step("Wait for 120 secs and verify that LSA's are not refreshed. ")
+ # get LSA age
+ dut = "r1"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {"lsaId": "100.1.1.0", "lsaAge": "get"},
+ ]
+ }
+ },
+ }
+ sleep(10)
+
+ result1 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+ sleep(5)
+ result2 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+
+ if result2 is not result1:
+ result = True
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Enable flood reduction on each router with 10 secs delay of between each router."
+ )
+ for rtr in topo["routers"].keys():
+ ospf_flood = {
+ rtr: {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}}
+ }
+ sleep(10)
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr)
+
+ step("Verify that LSA's are not refreshed. Do not age bit should be set to 1.")
+ dut = "r0"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsaId": "100.1.1.0",
+ "options": "*|-|DC|-|-|-|E|-",
+ },
+ ]
+ }
+ },
+ }
+ result = verify_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_flood_red_tc3_p0(request):
+ """Verify OSPF Flood reduction functionality between different area's"""
+ 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")
+ reset_config_on_routers(tgen)
+ red_static("r0")
+ step("Base config should be up, verify using OSPF convergence on all the routers")
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ 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("Enable flood reduction in area level on R0 in area 0")
+ ospf_flood = {
+ "r0": {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step("Verify that ospf lsa's are set with dc bit 1.")
+ dut = "r0"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsaId": "100.1.1.0",
+ "options": "*|-|DC|-|-|-|E|-",
+ },
+ ]
+ }
+ },
+ }
+ result = verify_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure the custom refresh timer")
+ ospf_flood = {"r0": {"ospf": {"lsa-refresh": 120}}}
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step(
+ "Enable flood. reduction in all the routers of the topology in area 0. Redistribute static route in area 0 R1 and area1 in R2."
+ )
+ for rtr in topo["routers"].keys():
+ ospf_flood = {
+ rtr: {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ sleep(10)
+
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that ospf lsa's are set with dc bit 1.")
+ for rtr in topo["routers"]:
+ dut = rtr
+ lsid = "{}".format(topo["routers"][rtr]["ospf"]["router_id"])
+ input_dict_db = {
+ "routerId": lsid,
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsaId": lsid,
+ "options": "*|-|DC|-|-|-|E|-",
+ },
+ ]
+ }
+ },
+ }
+ result = verify_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid=lsid
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Wait for 120 secs and verify that LSA's are not refreshed. ")
+ # get LSA age
+ dut = "r1"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {"lsaId": "100.1.1.0", "lsaAge": "get"},
+ ]
+ }
+ },
+ }
+ sleep(10)
+
+ result1 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+ sleep(5)
+ result2 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+
+ assert (result1 == result2) is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Enable flood reduction in area 1.")
+
+ ospf_flood = {
+ "r0": {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ ospf_flood = {
+ "r1": {
+ "ospf": {
+ "area": [
+ {"id": "0.0.0.0", "flood-reduction": True},
+ {"id": "0.0.0.1", "flood-reduction": True},
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ ospf_flood = {
+ "r2": {
+ "ospf": {
+ "area": [
+ {"id": "0.0.0.0", "flood-reduction": True},
+ {"id": "0.0.0.1", "flood-reduction": True},
+ {"id": "0.0.0.2", "flood-reduction": True},
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ ospf_flood = {
+ "r3": {
+ "ospf": {
+ "area": [
+ {"id": "0.0.0.0", "flood-reduction": True},
+ {"id": "0.0.0.2", "flood-reduction": True},
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that ospf lea's are set with dc bit 1.")
+ dut = "r1"
+ input_dict_db = {
+ "routerId": "100.1.1.1",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsaId": "100.1.1.1",
+ "options": "*|-|DC|-|-|-|E|-",
+ },
+ ]
+ }
+ },
+ }
+ result = verify_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.1"
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Wait for 120 secs and verify that LSA's are not refreshed. ")
+ # get LSA age
+ dut = "r1"
+ input_dict_db = {
+ "routerId": "100.1.1.1",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {"lsaId": "100.1.1.1", "lsaAge": "get"},
+ ]
+ }
+ },
+ }
+ sleep(10)
+
+ result1 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.1"
+ )
+ sleep(5)
+ result2 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.1"
+ )
+
+ if result2 is result1:
+ result = True
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Disable flood reduction in R0.")
+
+ ospf_flood = {
+ "r0": {
+ "ospf": {
+ "area": [{"id": "0.0.0.0", "flood-reduction": True, "delete": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ ospf_flood = {
+ "r1": {
+ "ospf": {
+ "area": [
+ {"id": "0.0.0.0", "flood-reduction": True, "delete": True},
+ {"id": "0.0.0.1", "flood-reduction": True, "delete": True},
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ ospf_flood = {
+ "r2": {
+ "ospf": {
+ "area": [
+ {"id": "0.0.0.0", "flood-reduction": True, "delete": True},
+ {"id": "0.0.0.1", "flood-reduction": True, "delete": True},
+ {"id": "0.0.0.2", "flood-reduction": True, "delete": True},
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ ospf_flood = {
+ "r3": {
+ "ospf": {
+ "area": [
+ {"id": "0.0.0.0", "flood-reduction": True, "delete": True},
+ {"id": "0.0.0.2", "flood-reduction": True, "delete": True},
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_flood)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ clear_ospf(tgen, "r0")
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that ospf lea's are not set with dc bit 1.")
+ dut = "r0"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsaId": "100.1.1.0",
+ "options": "*|-|DC|-|-|-|E|-",
+ },
+ ]
+ }
+ },
+ }
+ result = verify_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0", expected=False
+ )
+
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Expected: OSPF LSA should not be set with DC bit in {} \n "
+ "Found: {}".format(tc_name, dut, result)
+ )
+
+ step("Wait for 120 secs and verify that LSA's are not refreshed. ")
+ # get LSA age
+ dut = "r1"
+ input_dict_db = {
+ "routerId": "100.1.1.0",
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {"lsaId": "100.1.1.0", "lsaAge": "get"},
+ ]
+ }
+ },
+ }
+ sleep(10)
+
+ result1 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+ sleep(5)
+ result2 = get_ospf_database(
+ tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0"
+ )
+
+ if result2 is not result1:
+ result = True
+ 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_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py
new file mode 100644
index 0000000..1358027
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py
@@ -0,0 +1,650 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+)
+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.
+
+ # 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 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": {"nbrState": "Full", "role": "DR"},
+ "r2": {"nbrState": "Full", "role": "DROther"},
+ "r3": {"nbrState": "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": {"nbrState": "Full", "role": "Backup"},
+ "r2": {"nbrState": "Full", "role": "DROther"},
+ "r3": {"nbrState": "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 priority 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": {"nbrState": "Full", "role": "Backup"},
+ "r2": {"nbrState": "Full", "role": "DROther"},
+ "r3": {"nbrState": "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 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": {"nbrState": "Full", "role": "Backup"},
+ "r2": {"nbrState": "Full", "role": "DROther"},
+ "r3": {"nbrState": "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": {"nbrState": "Full", "role": "DR"},
+ "r2": {"nbrState": "2-Way", "role": "DROther"},
+ "r3": {"nbrState": "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": {"nbrState": "Full", "role": "Backup"},
+ "r2": {"nbrState": "Full", "role": "DROther"},
+ "r3": {"nbrState": "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": {"nbrState": "Full", "role": "DR"},
+ "r2": {"nbrState": "Full", "role": "DROther"},
+ "r3": {"nbrState": "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": {"nbrState": "Full", "role": "Backup"},
+ "r2": {"nbrState": "Full", "role": "DROther"},
+ "r3": {"nbrState": "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": {"nbrState": "Full", "role": "Backup"},
+ "r2": {"nbrState": "Full", "role": "DROther"},
+ "r3": {"nbrState": "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": 10,
+ "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..d669e21
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py
@@ -0,0 +1,262 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+)
+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.
+
+ # 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 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, "Testcase 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..a90d7db
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py
@@ -0,0 +1,699 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+ 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.
+
+ # 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)
+
+ 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)
+
+
+def test_ospf_p2mp_tc_delay_reflood(request):
+ """OSPF IFSM -Verify "delay-reflood" parameter in p2mp network."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ r0 = tgen.gears["r0"]
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+
+ step("Verify for interface with network type P2MP that delay-reflood is configured")
+ r0.vtysh_multicmd(
+ "conf t\ninterface r0-r1-eth0\nip ospf network point-to-multipoint delay-reflood"
+ )
+
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "mcastMemberOspfAllRouters": True,
+ "ospfEnabled": True,
+ "networkType": "POINTOMULTIPOINT",
+ "p2mpDelayReflood": True,
+ }
+ },
+ "r2": {
+ "ospf": {
+ "mcastMemberOspfAllRouters": True,
+ "ospfEnabled": True,
+ "networkType": "POINTOMULTIPOINT",
+ "p2mpDelayReflood": False,
+ }
+ },
+ "r3": {
+ "ospf": {
+ "mcastMemberOspfAllRouters": True,
+ "ospfEnabled": True,
+ "networkType": "POINTOMULTIPOINT",
+ "p2mpDelayReflood": False,
+ }
+ },
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ delay_reflood_cfg = (
+ tgen.net["r0"]
+ .cmd(
+ 'vtysh -c "show running" | grep "^ ip ospf network point-to-multipoint delay-reflood"'
+ )
+ .rstrip()
+ )
+
+ assertmsg = "delay-reflood' configuration applied, but not present in configuration"
+ assert (
+ delay_reflood_cfg == " ip ospf network point-to-multipoint delay-reflood"
+ ), assertmsg
+
+ step("Verify for interface with network type P2MP that delay-reflood is removed")
+ r0.vtysh_multicmd(
+ "conf t\ninterface r0-r1-eth0\nip ospf network point-to-multipoint"
+ )
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "mcastMemberOspfAllRouters": True,
+ "ospfEnabled": True,
+ "networkType": "POINTOMULTIPOINT",
+ "p2mpDelayReflood": False,
+ }
+ },
+ "r2": {
+ "ospf": {
+ "mcastMemberOspfAllRouters": True,
+ "ospfEnabled": True,
+ "networkType": "POINTOMULTIPOINT",
+ "p2mpDelayReflood": False,
+ }
+ },
+ "r3": {
+ "ospf": {
+ "mcastMemberOspfAllRouters": True,
+ "ospfEnabled": True,
+ "networkType": "POINTOMULTIPOINT",
+ "p2mpDelayReflood": False,
+ }
+ },
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ delay_reflood_cfg = (
+ tgen.net["r0"]
+ .cmd(
+ 'vtysh -c "show running" | grep "^ ip ospf network point-to-multipoint delay-reflood"'
+ )
+ .rstrip()
+ )
+ assertmsg = (
+ "delay-reflood' configuration removed, but still present in configuration"
+ )
+ assert (
+ delay_reflood_cfg != " ip ospf network point-to-multipoint delay-reflood"
+ ), assertmsg
+
+ step(
+ "Verify for interface with network type P2MP that delay-reflood is removed with removal of network type"
+ )
+ r0.vtysh_multicmd(
+ "conf t\ninterface r0-r1-eth0\nip ospf network point-to-multipoint delay-reflood"
+ )
+ r0.vtysh_multicmd(
+ "conf t\ninterface r0-r1-eth0\nno ip ospf network point-to-multipoint"
+ )
+ r0.vtysh_multicmd(
+ "conf t\ninterface r0-r1-eth0\nip ospf network point-to-multipoint"
+ )
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "mcastMemberOspfAllRouters": True,
+ "ospfEnabled": True,
+ "networkType": "POINTOMULTIPOINT",
+ "p2mpDelayReflood": False,
+ }
+ },
+ "r2": {
+ "ospf": {
+ "mcastMemberOspfAllRouters": True,
+ "ospfEnabled": True,
+ "networkType": "POINTOMULTIPOINT",
+ "p2mpDelayReflood": False,
+ }
+ },
+ "r3": {
+ "ospf": {
+ "mcastMemberOspfAllRouters": True,
+ "ospfEnabled": True,
+ "networkType": "POINTOMULTIPOINT",
+ "p2mpDelayReflood": False,
+ }
+ },
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ delay_reflood_cfg = (
+ tgen.net["r0"]
+ .cmd(
+ 'vtysh -c "show running" | grep "^ ip ospf network point-to-multipoint delay-reflood"'
+ )
+ .rstrip()
+ )
+ assertmsg = (
+ "delay-reflood' configuration removed, but still present in configuration"
+ )
+ assert (
+ delay_reflood_cfg != " ip ospf network point-to-multipoint delay-reflood"
+ ), assertmsg
+
+ 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": [
+ {
+ "nbrState": "Full/DROther",
+ }
+ ],
+ "100.1.1.2": [
+ {
+ "nbrState": "Full/DROther",
+ }
+ ],
+ "100.1.1.3": [
+ {
+ "nbrState": "Full/DROther",
+ }
+ ],
+ }
+ },
+ ],
+ [
+ "r1",
+ "show ip ospf n json",
+ {
+ "neighbors": {
+ "100.1.1.0": [
+ {
+ "nbrState": "Full/DROther",
+ }
+ ],
+ "100.1.1.2": [
+ {
+ "nbrState": "Full/DROther",
+ }
+ ],
+ "100.1.1.3": [
+ {
+ "nbrState": "Full/DROther",
+ }
+ ],
+ }
+ },
+ ],
+ [
+ "r2",
+ "show ip ospf n json",
+ {
+ "neighbors": {
+ "100.1.1.0": [
+ {
+ "nbrState": "Full/DROther",
+ }
+ ],
+ "100.1.1.1": [
+ {
+ "nbrState": "Full/DROther",
+ }
+ ],
+ "100.1.1.3": [
+ {
+ "nbrState": "Full/DROther",
+ }
+ ],
+ }
+ },
+ ],
+ [
+ "r3",
+ "show ip ospf n json",
+ {
+ "neighbors": {
+ "100.1.1.0": [
+ {
+ "nbrState": "Full/DROther",
+ }
+ ],
+ "100.1.1.1": [
+ {
+ "nbrState": "Full/DROther",
+ }
+ ],
+ "100.1.1.2": [
+ {
+ "nbrState": "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..c9f43cd
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py
@@ -0,0 +1,1296 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+)
+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.
+
+ # 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 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, "Testcase 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..f0950a2
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py
@@ -0,0 +1,778 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+)
+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.
+
+ # 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 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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..757d6fb
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py
@@ -0,0 +1,988 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+)
+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.
+
+ # 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)
+
+ 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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..ade5532
--- /dev/null
+++ b/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.py
@@ -0,0 +1,118 @@
+#!/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,
+)
+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.
+
+ # 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 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..b9ca022
--- /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": 4,
+ "priority": 98
+ }
+ },
+ "r1": {
+ "ipv4": "17.1.1.1/24",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 99
+ }
+ },
+ "r2": {
+ "ipv4": "17.1.1.3/24",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r3": {
+ "ipv4": "17.1.1.4/24",
+ "ospf": {
+ "area": "0.0.0.0",
+ "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"
+ }
+ },
+ "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": {}
+ }
+ }
+ }
+ }
+}
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..7937428
--- /dev/null
+++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py
@@ -0,0 +1,380 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+ 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.
+
+ # 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)
+
+ 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",
+ "restartSupport": "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",
+ "restartSupport": "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..46c0da3
--- /dev/null
+++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py
@@ -0,0 +1,356 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+ 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.
+
+ # 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)
+
+ 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 priority 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": {"nbrState": "Full", "role": "Backup"},
+ "r2": {"nbrState": "Full", "role": "DROther"},
+ "r3": {"nbrState": "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 priority 0 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": {"nbrState": "Full", "role": "DR"},
+ "r2": {"nbrState": "2-Way", "role": "DROther"},
+ "r3": {"nbrState": "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..3be2819
--- /dev/null
+++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py
@@ -0,0 +1,310 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+ 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.
+
+ # 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)
+
+ 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..0212f9d
--- /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",
+ "ifaceAddress":"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..dc19e03
--- /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":" ",
+ "directlyAttachedTo":"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":" ",
+ "directlyAttachedTo":"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..3114660
--- /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",
+ "ifaceAddress":"10.0.1.1",
+ "ifaceName":"eth-rt1:10.0.1.2"
+ }
+ ],
+ "3.3.3.3":[
+ {
+ "converged":"Full",
+ "ifaceAddress":"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..3e2aba8
--- /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":" ",
+ "directlyAttachedTo":"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":" ",
+ "directlyAttachedTo":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"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..49a019d
--- /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",
+ "ifaceAddress":"10.0.2.2",
+ "ifaceName":"eth-rt2:10.0.2.3"
+ }
+ ],
+ "4.4.4.4":[
+ {
+ "converged":"Full",
+ "ifaceAddress":"10.0.3.4",
+ "ifaceName":"eth-rt4:10.0.3.3"
+ }
+ ],
+ "6.6.6.6":[
+ {
+ "converged":"Full",
+ "ifaceAddress":"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..c17cf65
--- /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":" ",
+ "directlyAttachedTo":"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":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"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..9ab49d7
--- /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",
+ "ifaceAddress":"10.0.3.3",
+ "ifaceName":"eth-rt3:10.0.3.4"
+ }
+ ],
+ "5.5.5.5":[
+ {
+ "converged":"Full",
+ "ifaceAddress":"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..74de027
--- /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":" ",
+ "directlyAttachedTo":"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":" ",
+ "directlyAttachedTo":"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":" ",
+ "directlyAttachedTo":"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..7d3d589
--- /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",
+ "ifaceAddress":"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..5ecc222
--- /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":" ",
+ "directlyAttachedTo":"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":" ",
+ "directlyAttachedTo":"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..506eb40
--- /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",
+ "ifaceAddress":"10.0.4.3",
+ "ifaceName":"eth-rt3:10.0.4.6"
+ }
+ ],
+ "7.7.7.7":[
+ {
+ "converged":"Full",
+ "ifaceAddress":"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..50de685
--- /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":" ",
+ "directlyAttachedTo":"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":" ",
+ "directlyAttachedTo":"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":" ",
+ "directlyAttachedTo":"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..6429148
--- /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",
+ "ifaceAddress":"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..b8177d1
--- /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":" ",
+ "directlyAttachedTo":"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":" ",
+ "directlyAttachedTo":"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..73185d5
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py
@@ -0,0 +1,587 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_gr_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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 = 10
+ 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 = 10
+ 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")
+
+
+#
+# Test rt1 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt1():
+ logger.info("Test: verify rt1 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt1", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt1", ["ospfd"])
+
+ expect_grace_lsa(restarting="1.1.1.1", area="0.0.0.1", helper="rt2")
+ ensure_gr_is_in_zebra("rt1")
+ check_routers(restarting="rt1")
+
+
+#
+# Test rt2 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt2():
+ logger.info("Test: verify rt2 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt2", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt2", ["ospfd"])
+
+ 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")
+ check_routers(restarting="rt2")
+
+
+#
+# Test rt3 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt3():
+ logger.info("Test: verify rt3 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt3", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt3", ["ospfd"])
+
+ 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")
+ check_routers(restarting="rt3")
+
+
+#
+# Test rt4 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt4():
+ logger.info("Test: verify rt4 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt4", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt4", ["ospfd"])
+
+ 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")
+ check_routers(restarting="rt4")
+
+
+#
+# Test rt5 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt5():
+ logger.info("Test: verify rt5 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt5", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt5", ["ospfd"])
+
+ expect_grace_lsa(restarting="5.5.5.5", area="0.0.0.2", helper="rt4")
+ ensure_gr_is_in_zebra("rt5")
+ check_routers(restarting="rt5")
+
+
+#
+# Test rt6 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt6():
+ logger.info("Test: verify rt6 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt6", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt6", ["ospfd"])
+
+ 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")
+ check_routers(restarting="rt6")
+
+
+#
+# Test rt7 performing an unplanned graceful restart
+#
+def test_unplanned_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)
+
+ kill_router_daemons(tgen, "rt7", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt7", ["ospfd"])
+
+ expect_grace_lsa(restarting="7.7.7.7", area="0.0.0.3", helper="rt6")
+ ensure_gr_is_in_zebra("rt7")
+ 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..9dfe5e1
--- /dev/null
+++ b/tests/topotests/ospf_instance_redistribute/test_ospf_instance_redistribute.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_instance_redistribute.py
+#
+# Copyright (c) 2022 by
+# Nvidia, Inc.
+# Donald Sharp
+#
+
+"""
+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_metric_propagation/__init__.py b/tests/topotests/ospf_metric_propagation/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/__init__.py
diff --git a/tests/topotests/ospf_metric_propagation/h1/frr.conf b/tests/topotests/ospf_metric_propagation/h1/frr.conf
new file mode 100644
index 0000000..1196a19
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/h1/frr.conf
@@ -0,0 +1,10 @@
+!
+hostname h1
+password zebra
+log file /tmp/h1-frr.log
+!
+ip route 0.0.0.0/0 10.0.91.1
+!
+interface h1-eth0
+ ip address 10.0.91.2/24
+! \ No newline at end of file
diff --git a/tests/topotests/ospf_metric_propagation/h2/frr.conf b/tests/topotests/ospf_metric_propagation/h2/frr.conf
new file mode 100644
index 0000000..f951fe6
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/h2/frr.conf
@@ -0,0 +1,10 @@
+!
+hostname h2
+password zebra
+log file /tmp/h2-frr.log
+!
+ip route 0.0.0.0/0 10.0.94.4
+!
+interface h2-eth0
+ ip address 10.0.94.2/24
+! \ No newline at end of file
diff --git a/tests/topotests/ospf_metric_propagation/r1/frr.conf b/tests/topotests/ospf_metric_propagation/r1/frr.conf
new file mode 100644
index 0000000..8523049
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/r1/frr.conf
@@ -0,0 +1,96 @@
+!
+hostname r1
+password zebra
+log file /tmp/r1-frr.log
+ip forwarding
+!
+interface r1-eth0
+ ip address 10.0.1.1/24
+ ip ospf cost 100
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r1-eth1 vrf blue
+ ip address 10.0.10.1/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+interface r1-eth2 vrf green
+ ip address 10.0.91.1/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+router ospf
+ ospf router-id 10.0.255.1
+ distance 20
+ redistribute bgp route-map costplus
+ network 10.0.1.0/24 area 0
+!
+router ospf vrf blue
+ ospf router-id 10.0.255.1
+ distance 20
+ redistribute bgp route-map costplus
+ network 10.0.10.0/24 area 0
+!
+router ospf vrf green
+ ospf router-id 10.0.255.1
+ distance 20
+ redistribute bgp route-map costplus
+ network 10.0.91.0/24 area 0
+!
+router bgp 99
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf route-map rmap
+ import vrf route-map rmap
+ import vrf blue
+ import vrf green
+ !
+!
+router bgp 99 vrf blue
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf route-map rmap
+ import vrf route-map rmap
+ import vrf default
+ import vrf green
+ !
+router bgp 99 vrf green
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf route-map rmap
+ import vrf default
+ import vrf blue
+ !
+!
+route-map rmap permit 10
+ set metric-type type-1
+ set metric +1
+ exit
+!
+ip prefix-list min seq 5 permit 10.0.80.0/24
+route-map costmax permit 20
+ set metric-type type-1
+ set metric +1
+ set metric-min 713
+ match ip address prefix-list min
+ exit
+!
+ip prefix-list max seq 10 permit 10.0.70.0/24
+route-map costplus permit 30
+ set metric-type type-1
+ set metric +1
+ set metric-max 13
+ match ip address prefix-list max
+ exit
+!
+route-map costplus permit 40
+ set metric-type type-1
+ set metric +1
+ exit \ No newline at end of file
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json
new file mode 100644
index 0000000..4f1ced8
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json
@@ -0,0 +1,35 @@
+{
+ "10.0.94.0/24":[
+ {
+ "prefix":"10.0.94.0/24",
+ "prefixLen":24,
+ "protocol":"bgp",
+ "vrfName":"green",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":34,
+ "installed":true,
+ "table":12,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthopGroupId":"*",
+ "installedNexthopGroupId":"*",
+ "uptime":"*",
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"10.0.10.5",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "vrf":"blue",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json
new file mode 100644
index 0000000..882d3ca
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json
@@ -0,0 +1,35 @@
+{
+ "10.0.94.0/24":[
+ {
+ "prefix":"10.0.94.0/24",
+ "prefixLen":24,
+ "protocol":"bgp",
+ "vrfName":"green",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":136,
+ "installed":true,
+ "table":12,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthopGroupId":"*",
+ "installedNexthopGroupId":"*",
+ "uptime":"*",
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "vrf":"default",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json
new file mode 100644
index 0000000..cd52845
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json
@@ -0,0 +1,35 @@
+{
+ "10.0.94.0/24":[
+ {
+ "prefix":"10.0.94.0/24",
+ "prefixLen":24,
+ "protocol":"bgp",
+ "vrfName":"green",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":1138,
+ "installed":true,
+ "table":12,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthopGroupId":"*",
+ "installedNexthopGroupId":"*",
+ "uptime":"*",
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "vrf":"default",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json
new file mode 100644
index 0000000..133f375
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json
@@ -0,0 +1,35 @@
+{
+ "10.0.94.0/24":[
+ {
+ "prefix":"10.0.94.0/24",
+ "prefixLen":24,
+ "protocol":"bgp",
+ "vrfName":"green",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":1218,
+ "installed":true,
+ "table":12,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthopGroupId":"*",
+ "installedNexthopGroupId":"*",
+ "uptime":"*",
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "vrf":"default",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json
new file mode 100644
index 0000000..5d80509
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json
@@ -0,0 +1,35 @@
+{
+ "10.0.94.0/24":[
+ {
+ "prefix":"10.0.94.0/24",
+ "prefixLen":24,
+ "protocol":"bgp",
+ "vrfName":"green",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":238,
+ "installed":true,
+ "table":12,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthopGroupId":"*",
+ "installedNexthopGroupId":"*",
+ "uptime":"*",
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "vrf":"default",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json
new file mode 100644
index 0000000..1b59707
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json
@@ -0,0 +1,35 @@
+{
+ "10.0.94.0/24":[
+ {
+ "prefix":"10.0.94.0/24",
+ "prefixLen":24,
+ "protocol":"bgp",
+ "vrfName":"green",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":136,
+ "installed":true,
+ "table":12,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthopGroupId":"*",
+ "installedNexthopGroupId":"*",
+ "uptime":"*",
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"10.0.10.5",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "vrf":"blue",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_metric_propagation/r2/frr.conf b/tests/topotests/ospf_metric_propagation/r2/frr.conf
new file mode 100644
index 0000000..e67a374
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/r2/frr.conf
@@ -0,0 +1,81 @@
+!
+hostname r2
+password zebra
+log file /tmp/r2-frr.log
+ip forwarding
+!
+interface r2-eth0
+ ip address 10.0.1.2/24
+ ip ospf cost 100
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r2-eth1 vrf blue
+ ip address 10.0.20.2/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r2-eth2 vrf green
+ ip address 10.0.70.2/24
+ ip ospf cost 1000
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+ ospf router-id 10.0.255.2
+ distance 20
+ redistribute bgp route-map costplus
+ network 10.0.1.0/24 area 0
+!
+router ospf vrf blue
+ ospf router-id 10.0.255.2
+ distance 20
+ redistribute bgp route-map costplus
+ network 10.0.20.0/24 area 0
+!
+
+router ospf vrf green
+ ospf router-id 10.0.255.2
+ distance 20
+ redistribute bgp route-map costplus
+ network 10.0.70.0/24 area 0
+!
+
+router bgp 99
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf route-map rmap
+ import vrf blue
+ import vrf green
+ !
+!
+router bgp 99 vrf blue
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf route-map rmap
+ import vrf default
+ import vrf green
+ !
+router bgp 99 vrf green
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf route-map rmap
+ import vrf default
+ import vrf blue
+ !
+!
+route-map rmap permit 10
+ set metric-type type-1
+ set metric +1
+ exit
+!
+route-map costplus permit 1
+ set metric-type type-1
+ set metric +1
+ exit
diff --git a/tests/topotests/ospf_metric_propagation/r3/frr.conf b/tests/topotests/ospf_metric_propagation/r3/frr.conf
new file mode 100644
index 0000000..175851d
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/r3/frr.conf
@@ -0,0 +1,79 @@
+!
+hostname r3
+password zebra
+log file /tmp/r3-frr.log
+ip forwarding
+!
+interface r3-eth0
+ ip address 10.0.3.3/24
+ ip ospf cost 100
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r3-eth1 vrf blue
+ ip address 10.0.30.3/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r3-eth2 vrf green
+ ip address 10.0.80.3/24
+ ip ospf cost 1000
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+ ospf router-id 10.0.255.3
+ distance 20
+ redistribute bgp route-map costplus
+ network 10.0.3.0/24 area 0
+!
+router ospf vrf blue
+ ospf router-id 10.0.255.3
+ distance 20
+ redistribute bgp route-map costplus
+ network 10.0.30.0/24 area 0
+!
+router ospf vrf green
+ ospf router-id 10.0.255.3
+ distance 20
+ redistribute bgp route-map costplus
+ network 10.0.80.0/24 area 0
+!
+router bgp 99
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf route-map rmap
+ import vrf blue
+ import vrf green
+ !
+!
+router bgp 99 vrf blue
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf route-map rmap
+ import vrf default
+ import vrf green
+ !
+router bgp 99 vrf green
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf route-map rmap
+ import vrf default
+ import vrf blue
+ !
+!
+route-map rmap permit 10
+ set metric-type type-1
+ set metric +1
+ exit
+!
+route-map costplus permit 1
+ set metric-type type-1
+ set metric +1
+ exit
diff --git a/tests/topotests/ospf_metric_propagation/r4/frr.conf b/tests/topotests/ospf_metric_propagation/r4/frr.conf
new file mode 100644
index 0000000..70a47e3
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/r4/frr.conf
@@ -0,0 +1,78 @@
+!
+hostname r4
+password zebra
+log file /tmp/r4-frr.log
+ip forwarding
+!
+interface r4-eth0
+ ip address 10.0.3.4/24
+ ip ospf cost 100
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r4-eth1 vrf blue
+ ip address 10.0.40.4/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r4-eth2 vrf green
+ ip address 10.0.94.4/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+ ospf router-id 10.0.255.4
+ distance 20
+ redistribute bgp route-map costplus
+ network 10.0.3.0/24 area 0
+!
+router ospf vrf blue
+ ospf router-id 10.0.255.4
+ distance 20
+ redistribute bgp route-map costplus
+ network 10.0.40.0/24 area 0
+!
+router ospf vrf green
+ ospf router-id 10.0.255.1
+ distance 20
+ redistribute bgp route-map costplus
+ network 10.0.94.0/24 area 0
+!
+router bgp 99
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf route-map costplus
+ import vrf route-map rmap
+ import vrf blue
+ import vrf green
+ !
+!
+router bgp 99 vrf blue
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf route-map rmap
+ import vrf default
+ import vrf green
+ !
+router bgp 99 vrf green
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf route-map rmap
+ import vrf default
+ import vrf blue
+ !
+!
+route-map rmap permit 10
+ set metric-type type-1
+ set metric +1
+ exit
+!
+route-map costplus permit 1
+ set metric-type type-1
+ set metric +1
+ exit
diff --git a/tests/topotests/ospf_metric_propagation/ra/frr.conf b/tests/topotests/ospf_metric_propagation/ra/frr.conf
new file mode 100644
index 0000000..7be9e5c
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/ra/frr.conf
@@ -0,0 +1,27 @@
+!
+hostname ra
+password zebra
+log file /tmp/ra-frr.log
+ip forwarding
+!
+interface ra-eth0
+ ip address 10.0.50.5/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface ra-eth1
+ ip address 10.0.10.5/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface ra-eth2
+ ip address 10.0.20.5/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+ ospf router-id 10.0.255.5
+ network 10.0.10.0/24 area 0
+ network 10.0.20.0/24 area 0
+ network 10.0.50.0/24 area 0
+!
diff --git a/tests/topotests/ospf_metric_propagation/rb/frr.conf b/tests/topotests/ospf_metric_propagation/rb/frr.conf
new file mode 100644
index 0000000..a7dbf82
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/rb/frr.conf
@@ -0,0 +1,27 @@
+!
+hostname rb
+password zebra
+log file /tmp/rb-frr.log
+ip forwarding
+!
+interface rb-eth0
+ ip address 10.0.50.6/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface rb-eth1
+ ip address 10.0.30.6/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface rb-eth2
+ ip address 10.0.40.6/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+ ospf router-id 10.0.255.6
+ network 10.0.30.0/24 area 0
+ network 10.0.40.0/24 area 0
+ network 10.0.50.0/24 area 0
+!
diff --git a/tests/topotests/ospf_metric_propagation/rc/frr.conf b/tests/topotests/ospf_metric_propagation/rc/frr.conf
new file mode 100644
index 0000000..f5a2ed7
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/rc/frr.conf
@@ -0,0 +1,21 @@
+!
+hostname rc
+password zebra
+log file /tmp/rc-frr.log
+ip forwarding
+!
+interface rc-eth0
+ ip address 10.0.70.7/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface rc-eth1
+ ip address 10.0.80.7/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+ ospf router-id 10.0.255.7
+ network 10.0.70.0/24 area 0
+ network 10.0.80.0/24 area 0
+!
diff --git a/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py b/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py
new file mode 100644
index 0000000..085eb1f
--- /dev/null
+++ b/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py
@@ -0,0 +1,385 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_metric_propagation.py
+#
+# Copyright (c) 2023 ATCorp
+# Jafar Al-Gharaibeh
+#
+
+import os
+import sys
+import json
+from time import sleep
+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_metric_propagation.py: Test OSPF/BGP metric propagation
+"""
+
+TOPOLOGY = """
+ +-----+ +-----+
+ eth1 | | eth0 | | eth2
+ +-------------+ rA +---------------------------+ rB +---------------+
+ | .5 | | .5 .6 | | .6 |
+ | +--+--+ 10.0.50.0/24 +--+--+ .6 |
+ | |.5 |.6 |
+ | eth2| eth1| |
+ 10.0.10.0/24 | | |
+ | 10.0.20.0/24 10.0.30.0/24 10.0.40.0/24
+ |blue |blue |blue |blue
+ | | | |
+ eth1|.1 eth1|.2 eth1|.3 eth1|.4
+ +-----+ +--+--+ +--+--+ +-----+ +-+---+ +-+---+ +------+
+ | |eth0 eth2| | eth0 | |eth2 eth1| |eth2 eth3| | eth0 | |eth2 eth0| |
+ | h1 +----------+ R1 +----------+ R2 +-----------+ rC +----------+ R3 +------------+ R4 +---------+ h2 |
+ | | | | | | | | | | | | | |
+ +-----+.2 .1 +-----+.1 .2+-----+.2 .7 +-----+.7 .3+-----+.3 .4+-----+.4 .2+------+
+ green green green green
+
+ 10.0.91.0/24 10.0.1.0/24 10.0.70.0/24 10.0.80.0/24 10.0.3.0/24 10.0.94.0/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))
+
+ tgen.add_router("ra")
+ tgen.add_router("rb")
+ tgen.add_router("rc")
+ tgen.add_router("h1")
+ tgen.add_router("h2")
+
+ # Interconect router 1, 2
+ switch = tgen.add_switch("s1-2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 3, 4
+ switch = tgen.add_switch("s3-4")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+ # Interconect router a, b
+ switch = tgen.add_switch("sa-b")
+ switch.add_link(tgen.gears["ra"])
+ switch.add_link(tgen.gears["rb"])
+
+ # Interconect router 1, a
+ switch = tgen.add_switch("s1-a")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["ra"])
+
+ # Interconect router 2, a
+ switch = tgen.add_switch("s2-a")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["ra"])
+
+ # Interconect router 3, b
+ switch = tgen.add_switch("s3-b")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["rb"])
+
+ # Interconect router 4, b
+ switch = tgen.add_switch("s4-b")
+ switch.add_link(tgen.gears["r4"])
+ switch.add_link(tgen.gears["rb"])
+
+ # Interconect router 1, h1
+ switch = tgen.add_switch("s1-h1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["h1"])
+
+ # Interconect router 4, h2
+ switch = tgen.add_switch("s4-h2")
+ switch.add_link(tgen.gears["r4"])
+ switch.add_link(tgen.gears["h2"])
+
+ # Interconect router 2, c
+ switch = tgen.add_switch("s2-c")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["rc"])
+
+ # Interconect router 3, c
+ switch = tgen.add_switch("s3-c")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["rc"])
+
+
+def setup_module(mod):
+ logger.info("OSPF Metric Propagation:\n {}".format(TOPOLOGY))
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ vrf_setup_cmds = [
+ "ip link add name blue type vrf table 11",
+ "ip link set dev blue up",
+ "ip link add name green type vrf table 12",
+ "ip link set dev green up",
+ ]
+
+ # Starting Routers
+ router_list = tgen.routers()
+
+ # Create VRFs and bind to interfaces
+ for routern in range(1, 5):
+ for cmd in vrf_setup_cmds:
+ tgen.net["r{}".format(routern)].cmd(cmd)
+ for routern in range(1, 5):
+ tgen.net["r{}".format(routern)].cmd(
+ "ip link set dev r{}-eth1 vrf blue up".format(routern)
+ )
+ tgen.net["r{}".format(routern)].cmd(
+ "ip link set dev r{}-eth2 vrf green up".format(routern)
+ )
+
+ 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.20"):
+ tgen.set_error("unsupported version")
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_all_links_up():
+ "Test path R1 -> Ra -> Rb -> R4"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ r1 = tgen.gears["r1"]
+ json_file = "{}/r1/show_ip_route-1.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+ assertmsg = "r1 JSON output mismatches"
+ assert result is None, assertmsg
+
+
+def test_link_1_down():
+ "Test path R1 -> R2 -> Ra -> Rb -> R4"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ tgen.net["r1"].cmd("ip link set dev r1-eth1 down")
+ r1 = tgen.gears["r1"]
+
+ json_file = "{}/r1/show_ip_route-2.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+ assertmsg = "r1 JSON output mismatches"
+ assert result is None, assertmsg
+
+
+def test_link_1_2_down():
+ "Test path R1 -> R2 -> Rc -> R3 -> Rb -> R4"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ tgen.net["r2"].cmd("ip link set dev r2-eth1 down")
+ tgen.net["r1"].cmd("ip link set dev r1-eth0 down")
+ tgen.net["r2"].cmd("ip link set dev r2-eth2 down")
+ tgen.net["r2"].cmd("ip link set dev r2-eth2 up")
+ tgen.net["r1"].cmd("ip link set dev r1-eth0 up")
+ r1 = tgen.gears["r1"]
+
+ json_file = "{}/r1/show_ip_route-3.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+ assertmsg = "r1 JSON output mismatches"
+ assert result is None, assertmsg
+
+
+def test_link_1_2_3_down():
+ "Test path R1 -> R2 -> Rc -> R3 -> R4"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ tgen.net["r3"].cmd("ip link set dev r3-eth1 down")
+ tgen.net["r1"].cmd("ip link set dev r1-eth0 down")
+ tgen.net["r3"].cmd("ip link set dev r3-eth0 down")
+ tgen.net["r3"].cmd("ip link set dev r3-eth0 up")
+ tgen.net["r1"].cmd("ip link set dev r1-eth0 up")
+ r1 = tgen.gears["r1"]
+
+ json_file = "{}/r1/show_ip_route-4.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+ assertmsg = "r1 JSON output mismatches"
+ assert result is None, assertmsg
+
+
+def test_link_1_2_3_4_down():
+ "Test path R1 -> R2 -> Rc -> R3 -> R4"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ tgen.net["r4"].cmd("ip link set dev r4-eth1 down")
+ r1 = tgen.gears["r1"]
+
+ json_file = "{}/r1/show_ip_route-4.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+ assertmsg = "r1 JSON output mismatches"
+ assert result is None, assertmsg
+
+
+def test_link_1_2_4_down():
+ "Test path R1 -> R2 -> Rc -> R3 -> R4"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # bring link 3 back up
+ tgen.net["r3"].cmd("ip link set dev r3-eth1 up")
+ r1 = tgen.gears["r1"]
+
+ json_file = "{}/r1/show_ip_route-4.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+ assertmsg = "r1 JSON output mismatches"
+ assert result is None, assertmsg
+
+
+def test_link_1_4_down():
+ "Test path R1 -> R2 -> Ra -> Rb -> R3 -> R4"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # bring back link 2 up
+ tgen.net["r2"].cmd("ip link set dev r2-eth1 up")
+ r1 = tgen.gears["r1"]
+
+ json_file = "{}/r1/show_ip_route-5.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+ assertmsg = "r1 JSON output mismatches"
+ assert result is None, assertmsg
+
+
+def test_link_4_down():
+ "Test path R1 -> Ra -> Rb -> R3 -> R4"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # bring back link 1 up
+ tgen.net["r1"].cmd("ip link set dev r1-eth1 up")
+ r1 = tgen.gears["r1"]
+
+ json_file = "{}/r1/show_ip_route-6.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+ assertmsg = "r1 JSON output mismatches"
+ assert result is None, assertmsg
+
+
+def test_link_1_2_3_4_up():
+ "Test path R1 -> Ra -> Rb -> R4"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # bring back link 4 up
+ tgen.net["r4"].cmd("ip link set dev r4-eth1 up")
+ r1 = tgen.gears["r1"]
+
+ json_file = "{}/r1/show_ip_route-1.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+ assertmsg = "r1 JSON output mismatches"
+ 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/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..792304e
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/test_ospf_multi_vrf_bgp_route_leak.py
@@ -0,0 +1,230 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_multi_vrf_bgp_route_leak.py
+#
+# Copyright (c) 2022 ATCorp
+# Jafar Al-Gharaibeh
+#
+
+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..2716f63
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py
@@ -0,0 +1,311 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_netns_vrf.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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_nssa_topo1/__init__.py b/tests/topotests/ospf_nssa_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/__init__.py
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf
new file mode 100644
index 0000000..6d23c84
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf
@@ -0,0 +1,22 @@
+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
+!
+interface eth-rt2
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 12
+!
+router ospf
+ router-id 1.1.1.1
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf
new file mode 100644
index 0000000..7ba3dc7
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf
@@ -0,0 +1,6 @@
+log file staticd.log
+!
+hostname rt1
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref
new file mode 100644
index 0000000..ac34417
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref
@@ -0,0 +1,115 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref
new file mode 100644
index 0000000..ac34417
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref
@@ -0,0 +1,115 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref
new file mode 100644
index 0000000..ac34417
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref
@@ -0,0 +1,115 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref
new file mode 100644
index 0000000..ac34417
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref
@@ -0,0 +1,115 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref
new file mode 100644
index 0000000..6a05555
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref
@@ -0,0 +1,103 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref
new file mode 100644
index 0000000..6a05555
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref
@@ -0,0 +1,103 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref
new file mode 100644
index 0000000..2d3c8c4
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref
@@ -0,0 +1,91 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref
new file mode 100644
index 0000000..6a05555
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref
@@ -0,0 +1,103 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref
new file mode 100644
index 0000000..f41ee3b
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref
@@ -0,0 +1,103 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":1000,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref
new file mode 100644
index 0000000..2d3c8c4
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref
@@ -0,0 +1,91 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..1df1005
--- /dev/null
+++ b/tests/topotests/ospf_nssa_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-rt2
+ ip address 10.0.1.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf
new file mode 100644
index 0000000..12884d2
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf
@@ -0,0 +1,35 @@
+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
+ ip ospf area 0
+!
+interface eth-rt1
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 12
+!
+interface eth-rt3
+ ip ospf network point-to-point
+ ip ospf area 1
+ ip ospf hello-interval 3
+ ip ospf dead-interval 12
+!
+interface eth-rt4
+ ip ospf network point-to-point
+ ip ospf area 1
+ ip ospf hello-interval 3
+ ip ospf dead-interval 12
+!
+router ospf
+ router-id 2.2.2.2
+ area 1 nssa
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf
new file mode 100644
index 0000000..b6d4233
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf
@@ -0,0 +1,6 @@
+log file staticd.log
+!
+hostname rt2
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref
new file mode 100644
index 0000000..c094117
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref
@@ -0,0 +1,127 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "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":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt4"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref
new file mode 100644
index 0000000..c094117
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref
@@ -0,0 +1,127 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "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":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt4"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref
new file mode 100644
index 0000000..a67dfb4
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref
@@ -0,0 +1,139 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "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":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt4"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "0.0.0.0\/0":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":1,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref
new file mode 100644
index 0000000..c094117
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref
@@ -0,0 +1,127 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "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":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt4"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref
new file mode 100644
index 0000000..c7dd93c
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref
@@ -0,0 +1,129 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "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":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref
new file mode 100644
index 0000000..9c3cfff
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref
@@ -0,0 +1,117 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "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":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref
new file mode 100644
index 0000000..f6bbdfa
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref
@@ -0,0 +1,103 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "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":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt4"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref
new file mode 100644
index 0000000..c7dd93c
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref
@@ -0,0 +1,129 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "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":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref
new file mode 100644
index 0000000..c7dd93c
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref
@@ -0,0 +1,129 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "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":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref
new file mode 100644
index 0000000..c094117
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref
@@ -0,0 +1,127 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "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":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt4"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..fa274eb
--- /dev/null
+++ b/tests/topotests/ospf_nssa_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-rt1
+ ip address 10.0.1.2/24
+!
+interface eth-rt3
+ ip address 10.0.2.2/24
+!
+interface eth-rt4
+ ip address 10.0.3.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf
new file mode 100644
index 0000000..9691a7c
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf
@@ -0,0 +1,24 @@
+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
+ 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 12
+!
+router ospf
+ router-id 3.3.3.3
+ area 1 nssa
+ redistribute connected
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf
new file mode 100644
index 0000000..f0edd6c
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf
@@ -0,0 +1,8 @@
+log file staticd.log
+!
+hostname rt3
+!
+ip route 0.0.0.0/0 Null0
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref
new file mode 100644
index 0000000..a2d078a
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref
@@ -0,0 +1,138 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref
new file mode 100644
index 0000000..4619067
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref
@@ -0,0 +1,150 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":1000,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref
new file mode 100644
index 0000000..a2d078a
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref
@@ -0,0 +1,138 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref
new file mode 100644
index 0000000..a2d078a
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref
@@ -0,0 +1,138 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref
new file mode 100644
index 0000000..1038721
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref
@@ -0,0 +1,150 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref
new file mode 100644
index 0000000..4f8eaf1
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref
@@ -0,0 +1,138 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref
new file mode 100644
index 0000000..41e9f67
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref
@@ -0,0 +1,126 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref
new file mode 100644
index 0000000..1038721
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref
@@ -0,0 +1,150 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref
new file mode 100644
index 0000000..4619067
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref
@@ -0,0 +1,150 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":1000,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref
new file mode 100644
index 0000000..4619067
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref
@@ -0,0 +1,150 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":1000,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.1\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.2\/32":{
+ "routeType":"N E2",
+ "cost":20,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..d943540
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf
@@ -0,0 +1,18 @@
+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-rt2
+ ip address 10.0.2.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf
new file mode 100644
index 0000000..cba7cf7
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf
@@ -0,0 +1,24 @@
+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
+ 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 12
+!
+router ospf
+ router-id 4.4.4.4
+ area 1 nssa
+ redistribute static
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf
new file mode 100644
index 0000000..e00ee5d
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf
@@ -0,0 +1,9 @@
+log file staticd.log
+!
+hostname rt4
+!
+ip route 172.16.1.1/32 Null0
+ip route 172.16.1.2/32 Null0
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref
new file mode 100644
index 0000000..e57f542
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref
@@ -0,0 +1,114 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref
new file mode 100644
index 0000000..82a0e1a
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref
@@ -0,0 +1,126 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":1000,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref
new file mode 100644
index 0000000..e57f542
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref
@@ -0,0 +1,114 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref
new file mode 100644
index 0000000..e57f542
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref
@@ -0,0 +1,114 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref
new file mode 100644
index 0000000..5f51b3b
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref
@@ -0,0 +1,126 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref
new file mode 100644
index 0000000..5f51b3b
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref
@@ -0,0 +1,126 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref
new file mode 100644
index 0000000..5f51b3b
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref
@@ -0,0 +1,126 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref
new file mode 100644
index 0000000..5f51b3b
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref
@@ -0,0 +1,126 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":20,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref
new file mode 100644
index 0000000..82a0e1a
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref
@@ -0,0 +1,126 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":1000,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref
new file mode 100644
index 0000000..82a0e1a
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref
@@ -0,0 +1,126 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"lo"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directlyAttachedTo":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "type2cost":1000,
+ "tag":0,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..588febe
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf
@@ -0,0 +1,18 @@
+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
+ ip address 10.0.3.4/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py b/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py
new file mode 100644
index 0000000..432ddf0
--- /dev/null
+++ b/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py
@@ -0,0 +1,416 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_nssa_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2023 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+test_ospf_nssa_topo1.py:
+
+ +---------+
+ | RT1 |
+ | 1.1.1.1 |
+ +---------+
+ |eth-rt2
+ |
+ |10.0.1.0/24
+ |
+ |eth-rt1
+ +---------+
+ | RT2 |
+ | 2.2.2.2 |
+ +---------+
+ eth-rt3| |eth-rt4
+ | |
+ 10.0.2.0/24 | | 10.0.3.0/24
+ +---------+ +--------+
+ | |
+ |eth-rt2 |eth-rt2
+ +---------+ +---------+
+ | RT3 | | RT4 |
+ | 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.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4"]:
+ 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["rt2"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2")
+
+
+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_STATIC, os.path.join(CWD, "{}/staticd.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): test initial network convergence")
+ 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"]:
+ router_compare_json_output(
+ rname, "show ip ospf route json", "step1/show_ip_ospf_route.ref"
+ )
+
+
+#
+# Step 2
+#
+# Action(s):
+# -rt3: configure an NSSA default route
+#
+# Expected changes:
+# -rt2: add NSSA default route pointing to rt3
+#
+def test_rib_step2():
+ logger.info("Test (step 2): verify OSPF routes")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Adding NSSA default on rt4")
+ tgen.net["rt3"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa default-information-originate"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4"]:
+ router_compare_json_output(
+ rname, "show ip ospf route json", "step2/show_ip_ospf_route.ref"
+ )
+
+
+#
+# Step 3
+#
+# Action(s):
+# -rt3: remove NSSA default route
+#
+# Expected changes:
+# -rt2: remove NSSA default route
+#
+def test_rib_step3():
+ logger.info("Test (step 3): verify OSPF routes")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Removing NSSA default on rt4")
+ tgen.net["rt3"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4"]:
+ router_compare_json_output(
+ rname, "show ip ospf route json", "step3/show_ip_ospf_route.ref"
+ )
+
+
+#
+# Step 4
+#
+# Action(s):
+# -rt2: configure an NSSA range for 172.16.1.0/24
+#
+# Expected changes:
+# -rt1: the 172.16.1.1/32 and 172.16.1.2/32 routes should be removed
+# -rt1: the 172.16.1.0/24 route should be added
+#
+def test_rib_step4():
+ logger.info("Test (step 4): verify OSPF routes")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring NSSA range on rt2")
+ tgen.net["rt2"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4"]:
+ router_compare_json_output(
+ rname, "show ip ospf route json", "step4/show_ip_ospf_route.ref"
+ )
+
+
+#
+# Step 5
+#
+# Action(s):
+# -rt4: remove the 172.16.1.1/32 static route
+#
+# Expected changes:
+# -None (the 172.16.1.0/24 range is still active because of 172.16.1.2/32)
+#
+def test_rib_step5():
+ logger.info("Test (step 5): verify OSPF routes")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Removing first static route in rt4")
+ tgen.net["rt4"].cmd('vtysh -c "conf t" -c "no ip route 172.16.1.1/32 Null0"')
+
+ for rname in ["rt1", "rt2", "rt3", "rt4"]:
+ router_compare_json_output(
+ rname, "show ip ospf route json", "step5/show_ip_ospf_route.ref"
+ )
+
+
+#
+# Step 6
+#
+# Action(s):
+# -rt4: remove the 172.16.1.2/32 static route
+#
+# Expected changes:
+# -rt1: remove the 172.16.1.0/24 route since the NSSA range is no longer active
+#
+def test_rib_step6():
+ logger.info("Test (step 6): verify OSPF routes")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Removing second static route in rt4")
+ tgen.net["rt4"].cmd('vtysh -c "conf t" -c "no ip route 172.16.1.2/32 Null0"')
+
+ for rname in ["rt1", "rt2", "rt3", "rt4"]:
+ router_compare_json_output(
+ rname, "show ip ospf route json", "step6/show_ip_ospf_route.ref"
+ )
+
+
+#
+# Step 7
+#
+# Action(s):
+# -rt4: readd the 172.16.1.1/32 and 172.16.1.2/32 static routes
+#
+# Expected changes:
+# -rt1: readd the 172.16.1.0/24 route since the NSSA range is active again
+#
+def test_rib_step7():
+ logger.info("Test (step 7): verify OSPF routes")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Readding static routes in rt4")
+ tgen.net["rt4"].cmd('vtysh -c "conf t" -c "ip route 172.16.1.1/32 Null0"')
+ tgen.net["rt4"].cmd('vtysh -c "conf t" -c "ip route 172.16.1.2/32 Null0"')
+
+ for rname in ["rt1", "rt2", "rt3", "rt4"]:
+ router_compare_json_output(
+ rname, "show ip ospf route json", "step7/show_ip_ospf_route.ref"
+ )
+
+
+#
+# Step 8
+#
+# Action(s):
+# -rt2: update the NSSA range with a static cost
+#
+# Expected changes:
+# -rt1: update the metric of the 172.16.1.0/24 route from 20 to 1000
+#
+def test_rib_step8():
+ logger.info("Test (step 8): verify OSPF routes")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Updating the NSSA range cost on rt2")
+ tgen.net["rt2"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24 cost 1000"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4"]:
+ router_compare_json_output(
+ rname, "show ip ospf route json", "step8/show_ip_ospf_route.ref"
+ )
+
+
+#
+# Step 9
+#
+# Action(s):
+# -rt2: update the NSSA range to not advertise itself
+#
+# Expected changes:
+# -rt1: the 172.16.1.0/24 route should be removed
+#
+def test_rib_step9():
+ logger.info("Test (step 9): verify OSPF routes")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Updating the NSSA range to not advertise itself")
+ tgen.net["rt2"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24 not-advertise"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4"]:
+ router_compare_json_output(
+ rname, "show ip ospf route json", "step9/show_ip_ospf_route.ref"
+ )
+
+
+#
+# Step 10
+#
+# Action(s):
+# -rt2: remove the NSSA range
+#
+# Expected changes:
+# -rt1: the 172.16.1.1/32 and 172.16.1.2/32 routes should be added
+#
+def test_rib_step10():
+ logger.info("Test (step 10): verify OSPF routes")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Removing NSSA range on rt2")
+ tgen.net["rt2"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "no area 1 nssa range 172.16.1.0/24"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4"]:
+ router_compare_json_output(
+ rname, "show ip ospf route json", "step10/show_ip_ospf_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/ospf_prefix_suppression/r1/frr.conf b/tests/topotests/ospf_prefix_suppression/r1/frr.conf
new file mode 100644
index 0000000..437b474
--- /dev/null
+++ b/tests/topotests/ospf_prefix_suppression/r1/frr.conf
@@ -0,0 +1,47 @@
+!
+hostname r1
+password zebra
+log file /tmp/r1-frr.log
+ip forwarding
+!
+interface r1-eth0
+ ip address 10.1.1.1/24
+ ip ospf network broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r1-eth1
+ ip address 10.1.2.1/24
+ ip ospf network non-broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+interface r1-eth2
+ ip address 10.1.3.1/24
+ ip ospf network point-to-point
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r1-eth3
+ ip address 10.1.4.1/24
+ ip ospf network point-to-multipoint
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r1-eth4
+ ip address 10.1.7.1/24
+ ip ospf network broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+router ospf
+ ospf router-id 1.1.1.1
+ distance 20
+ network 10.1.1.0/24 area 0
+ network 10.1.2.0/24 area 0
+ network 10.1.3.0/24 area 0
+ network 10.1.4.0/24 area 0
+ network 10.1.7.0/24 area 0
+!
diff --git a/tests/topotests/ospf_prefix_suppression/r2/frr.conf b/tests/topotests/ospf_prefix_suppression/r2/frr.conf
new file mode 100644
index 0000000..68390f1
--- /dev/null
+++ b/tests/topotests/ospf_prefix_suppression/r2/frr.conf
@@ -0,0 +1,57 @@
+!
+hostname r2
+password zebra
+log file /tmp/r1-frr.log
+ip forwarding
+!
+interface r2-eth0
+ ip address 10.1.1.2/24
+ ip ospf network broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r2-eth1
+ ip address 10.1.2.2/24
+ ip ospf network non-broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+interface r2-eth2
+ ip address 10.1.3.2/24
+ ip ospf network point-to-point
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+interface r2-eth3
+ ip address 10.1.4.2/24
+ ip ospf network point-to-multipoint
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+interface r2-eth4
+ ip address 10.1.5.2/24
+ ip ospf network broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+interface r2-eth5
+ ip address 10.1.6.2/24
+ ip ospf network broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+router ospf
+ ospf router-id 2.2.2.2
+ distance 20
+ network 10.1.1.0/24 area 0
+ network 10.1.2.0/24 area 0
+ network 10.1.3.0/24 area 0
+ network 10.1.4.0/24 area 0
+ network 10.1.5.0/24 area 0
+ network 10.1.6.0/24 area 1
+!
diff --git a/tests/topotests/ospf_prefix_suppression/r3/frr.conf b/tests/topotests/ospf_prefix_suppression/r3/frr.conf
new file mode 100644
index 0000000..984a39d
--- /dev/null
+++ b/tests/topotests/ospf_prefix_suppression/r3/frr.conf
@@ -0,0 +1,25 @@
+!
+hostname r3
+password zebra
+log file /tmp/r1-frr.log
+ip forwarding
+!
+interface r3-eth0
+ ip address 10.1.5.3/24
+ ip ospf network broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+interface r3-eth1
+ ip address 10.1.6.3/24
+ ip ospf network broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+router ospf
+ ospf router-id 3.3.3.3
+ distance 20
+ network 10.1.5.0/24 area 0
+ network 10.1.6.0/24 area 1
diff --git a/tests/topotests/ospf_prefix_suppression/test_ospf_prefix_suppression.py b/tests/topotests/ospf_prefix_suppression/test_ospf_prefix_suppression.py
new file mode 100644
index 0000000..d5ea7eb
--- /dev/null
+++ b/tests/topotests/ospf_prefix_suppression/test_ospf_prefix_suppression.py
@@ -0,0 +1,951 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_prefix_suppression.py
+#
+# Copyright (c) 2023 LabN Consulting
+# Acee Lindem
+#
+
+import os
+import sys
+import json
+from time import sleep
+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
+
+from lib.common_config import (
+ run_frr_cmd,
+ shutdown_bringup_interface,
+ start_router_daemons,
+ step,
+)
+
+
+"""
+test_ospf_metric_propagation.py: Test OSPF/BGP metric propagation
+"""
+
+TOPOLOGY = """
+
+
+ +-----+ +-----+ +-----+
+ eth4 | | eth0 | | eth4 eth0 | |
+ ------+ +-------------+ +--------------+ |
+10.1.7.0/24 | | 10.1.1.0/24 | | 10.1.5.0/24 | |
+ | | | |.2 .3| |
+ | | eth1 | | | |
+ | +-------------+ | | |
+ | R1 | 10.1.2.0/24 | R2 | | R3 |
+ | | | | | |
+ | | eth2 | | | |
+ | +-------------+ | | |
+ | | 10.1.3.0/24 | | | |
+ | | | | | |
+ | | eth3 | | eth5 eth1 | |
+ | +-------------+ +--------------+ |
+ | | 10.1.4.0/24 | | 10.1.6.0/24 | |
+ .1 +-----+.1 .2+-----+.2 .3+-----+
+
+"""
+
+# 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 3 routers
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+ tgen.add_router("r3")
+
+ # Interconect router 1, 2 (0)
+ switch = tgen.add_switch("s1-1-2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 1, 2 (1)
+ switch = tgen.add_switch("s2-1-2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 1, 2 (2)
+ switch = tgen.add_switch("s3-1-2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 1, 2 (3)
+ switch = tgen.add_switch("s4-1-2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 2, 3 (0)
+ switch = tgen.add_switch("s5-2-3")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+ # Interconect router 2, 3 (1)
+ switch = tgen.add_switch("s6-2-3")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+ # Add standalone network to router 1
+ switch = tgen.add_switch("s7-1")
+ switch.add_link(tgen.gears["r1"])
+
+
+def setup_module(mod):
+ logger.info("OSPF Prefix Suppression:\n {}".format(TOPOLOGY))
+
+ tgen = Topogen(build_topo, mod.__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_frr_config(os.path.join(CWD, "{}/frr.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_all_routes_advertised():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("Skipped because of router(s) failure")
+
+ # Verify OSPF routes are installed
+ r3 = tgen.gears["r3"]
+ input_dict = {
+ "10.1.1.0/24": [
+ {
+ "prefix": "10.1.1.0/24",
+ "prefixLen": 24,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": "10.1.5.2",
+ "interfaceName": "r3-eth0",
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.1.0/24 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.1.0/24 not installed on router r3"
+ assert result is None, assertmsg
+
+ input_dict = {
+ "10.1.2.0/24": [
+ {
+ "prefix": "10.1.2.0/24",
+ "prefixLen": 24,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": "10.1.5.2",
+ "interfaceName": "r3-eth0",
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.2.0/24 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.2.0/24 not installed on router r3"
+ assert result is None, assertmsg
+
+ input_dict = {
+ "10.1.3.0/24": [
+ {
+ "prefix": "10.1.3.0/24",
+ "prefixLen": 24,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": "10.1.5.2",
+ "interfaceName": "r3-eth0",
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.3.0/24 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.3.0/24 not installed on router r3"
+ assert result is None, assertmsg
+
+ input_dict = {
+ "10.1.4.1/32": [
+ {
+ "prefix": "10.1.4.1/32",
+ "prefixLen": 32,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": "10.1.5.2",
+ "interfaceName": "r3-eth0",
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.4.1/32 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.4.1/32 not installed on router r3"
+ assert result is None, assertmsg
+
+ input_dict = {
+ "10.1.4.2/32": [
+ {
+ "prefix": "10.1.4.2/32",
+ "prefixLen": 32,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": "10.1.5.2",
+ "interfaceName": "r3-eth0",
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.4.2/32 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.4.2/32 not installed on router r3"
+ assert result is None, assertmsg
+
+ input_dict = {
+ "10.1.7.0/24": [
+ {
+ "prefix": "10.1.7.0/24",
+ "prefixLen": 24,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": "10.1.5.2",
+ "interfaceName": "r3-eth0",
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.7.0/24 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.7.0/24 not installed on router r3"
+ assert result is None, assertmsg
+
+ input_dict = {}
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.8.0/24 json", input_dict, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.8.0/24 installed on router r3"
+ assert result is None, assertmsg
+
+
+def test_broadcast_stub_suppression():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("Skipped because of router(s) failure")
+
+ step("Configure R1 interface r1-eth4 with prefix suppression")
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf t\ninterface r1-eth4\nip ospf prefix-suppression")
+
+ step("Verify the R1 configuration of 'ip ospf prefix-suppression'")
+ prefix_suppression_cfg = (
+ tgen.net["r1"]
+ .cmd('vtysh -c "show running ospfd" | grep "^ ip ospf prefix-suppression"')
+ .rstrip()
+ )
+ assertmsg = "'ip ospf prefix-suppression' applied, but not present in configuration"
+ assert prefix_suppression_cfg == " ip ospf prefix-suppression", assertmsg
+
+ step("Verify that ospf-prefix suppression is applied to the R1 interface")
+ r1_eth4_with_prefix_suppression = {
+ "interfaces": {
+ "r1-eth4": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.1.7.1",
+ "ospfIfType": "Broadcast",
+ "prefixSuppression": True,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip ospf interface r1-eth4 json",
+ r1_eth4_with_prefix_suppression,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "R1 OSPF interface r1-eth4 doesn't have prefix-suppression enabled"
+ assert result is None, assertmsg
+
+ step(
+ "Verify that ospf-prefix suppression is applied to the R1 interface (non-JSON)"
+ )
+ prefix_suppression_show = (
+ tgen.net["r1"]
+ .cmd(
+ 'vtysh -c "show ip ospf interface r1-eth4" | grep "^ Suppress advertisement of interface IP prefix"'
+ )
+ .rstrip()
+ )
+ assertmsg = (
+ "'ip ospf prefix-suppression' applied, but not present in interface show"
+ )
+ assert (
+ prefix_suppression_show == " Suppress advertisement of interface IP prefix"
+ ), assertmsg
+
+ step("Verify the ospf prefix is not advertised and not present on r3")
+ r3 = tgen.gears["r3"]
+ input_dict = {}
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.7.0/24 json", input_dict, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.7.0/24 installed on router r3"
+ assert result is None, assertmsg
+
+ step("Remove R1 interface r1-eth4 prefix-suppression configuration")
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf t\ninterface r1-eth4\nno ip ospf prefix-suppression")
+
+ step("Verify no R1 configuration of 'ip ospf prefix-suppression")
+ rc, _, _ = tgen.net["r1"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf prefix-suppression'", warn=False
+ )
+ assertmsg = (
+ "'ip ospf prefix-suppression' not applied, but present in R1 configuration"
+ )
+ assert rc, assertmsg
+
+ step("Verify that ospf-prefix suppression is not applied to the R1 interface")
+ r1_eth4_without_prefix_suppression = {
+ "interfaces": {
+ "r1-eth4": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.1.7.1",
+ "ospfIfType": "Broadcast",
+ "prefixSuppression": False,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip ospf interface r1-eth4 json",
+ r1_eth4_without_prefix_suppression,
+ )
+
+ step("Verify that 10.1.7.0/24 route is now installed on R3")
+ input_dict = {
+ "10.1.7.0/24": [
+ {
+ "prefix": "10.1.7.0/24",
+ "prefixLen": 24,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": "10.1.5.2",
+ "interfaceName": "r3-eth0",
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.7.0/24 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.7.0/24 not installed on router r3"
+ assert result is None, assertmsg
+
+
+def test_broadcast_transit_suppression():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("Skipped because of router(s) failure")
+
+ step(
+ "Configure R1 interface r1-eth0 with prefix suppression using interface address"
+ )
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf t\ninterface r1-eth0\nip ospf prefix-suppression 10.1.1.1")
+
+ step("Verify the R1 configuration of 'ip ospf prefix-suppression 10.1.1.1'")
+ prefix_suppression_cfg = (
+ tgen.net["r1"]
+ .cmd(
+ 'vtysh -c "show running ospfd" | grep "^ ip ospf prefix-suppression 10.1.1.1"'
+ )
+ .rstrip()
+ )
+ assertmsg = "'ip ospf prefix-suppression 10.1.1.1' applied, but not present in configuration"
+ assert prefix_suppression_cfg == " ip ospf prefix-suppression 10.1.1.1", assertmsg
+
+ step(
+ "Configure R2 interface r2-eth0 with prefix suppression using interface address"
+ )
+ r2 = tgen.gears["r2"]
+ r2.vtysh_cmd("conf t\ninterface r2-eth0\nip ospf prefix-suppression 10.1.1.2")
+
+ step("Verify that ospf-prefix suppression is applied to the R1 interface")
+ r1_eth0_with_prefix_suppression = {
+ "interfaces": {
+ "r1-eth0": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.1.1.1",
+ "ospfIfType": "Broadcast",
+ "networkType": "BROADCAST",
+ "prefixSuppression": True,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip ospf interface r1-eth0 json",
+ r1_eth0_with_prefix_suppression,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "R1 OSPF interface r1-eth0 doesn't have prefix-suppression enabled"
+ assert result is None, assertmsg
+
+ step("Verify the OSPF prefix is not advertised and not present on r3")
+ r3 = tgen.gears["r3"]
+ input_dict = {}
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.1.0/24 json", input_dict, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.1.0/24 installed on router r3"
+ assert result is None, assertmsg
+
+ step("Verify the OSPF Network-LSA prefixes are also not present on R3 ")
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.1.1/24 json", input_dict, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.1.1/24 installed on router r3"
+ assert result is None, assertmsg
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.1.2/24 json", input_dict, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.1.2/24 installed on router r3"
+ assert result is None, assertmsg
+
+ step(
+ "Remove R1 interface r1-eth0 prefix-suppression configuration using interface address"
+ )
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf t\ninterface r1-eth0\nno ip ospf prefix-suppression 10.1.1.1")
+
+ step(
+ "Remove R2 interface r2-eth0 prefix-suppression configuration using interface address"
+ )
+ r2 = tgen.gears["r2"]
+ r2.vtysh_cmd("conf t\ninterface r2-eth0\nno ip ospf prefix-suppression 10.1.1.2")
+
+ step("Verify no R1 configuration of 'ip ospf prefix-suppression")
+ rc, _, _ = tgen.net["r1"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf prefix-suppression 10.1.1.1'", warn=False
+ )
+ assertmsg = "'ip ospf prefix-suppression 10.1.1.1' not applied, but present in R1 configuration"
+ assert rc, assertmsg
+
+ step("Verify that ospf-prefix suppression is not applied to the R1 interface")
+ r1_eth0_without_prefix_suppression = {
+ "interfaces": {
+ "r1-eth0": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.1.1.1",
+ "ospfIfType": "Broadcast",
+ "networkType": "BROADCAST",
+ "prefixSuppression": False,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip ospf interface r1-eth0 json",
+ r1_eth0_without_prefix_suppression,
+ )
+
+ step("Verify that 10.1.1.0/24 route is now installed on R3")
+ input_dict = {
+ "10.1.1.0/24": [
+ {
+ "prefix": "10.1.1.0/24",
+ "prefixLen": 24,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": "10.1.5.2",
+ "interfaceName": "r3-eth0",
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.1.0/24 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.1.0/24 not installed on router r3"
+ assert result is None, assertmsg
+
+
+def test_nbma_transit_suppression():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("Skipped because of router(s) failure")
+
+ step("Configure R1 interface r1-eth1 with prefix suppression")
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf t\ninterface r1-eth1\nip ospf prefix-suppression")
+
+ step("Configure R2 interface r2-eth1 with prefix suppression")
+ r2 = tgen.gears["r2"]
+ r2.vtysh_cmd("conf t\ninterface r2-eth1\nip ospf prefix-suppression")
+
+ step("Verify that ospf-prefix suppression is applied to the R1 interface")
+ r1_eth1_with_prefix_suppression = {
+ "interfaces": {
+ "r1-eth1": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.1.2.1",
+ "ospfIfType": "Broadcast",
+ "networkType": "NBMA",
+ "prefixSuppression": True,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip ospf interface r1-eth1 json",
+ r1_eth1_with_prefix_suppression,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "R1 OSPF interface r1-eth1 doesn't have prefix-suppression enabled"
+ assert result is None, assertmsg
+
+ step("Verify the OSPF prefix is not advertised and not present on r3")
+ r3 = tgen.gears["r3"]
+ input_dict = {}
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.2.0/24 json", input_dict, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.2.0/24 installed on router r3"
+ assert result is None, assertmsg
+
+ step("Verify the OSPF Network-LSA prefixes are also not present on R3 ")
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.2.1/24 json", input_dict, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.2.1/24 installed on router r3"
+ assert result is None, assertmsg
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.2.2/24 json", input_dict, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.2.2/24 installed on router r3"
+ assert result is None, assertmsg
+
+ step("Remove R1 interface r1-eth1 prefix-suppression configuration")
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf t\ninterface r1-eth1\nno ip ospf prefix-suppression")
+
+ step("Remove R2 interface eth1 prefix-suppression configuration")
+ r2 = tgen.gears["r2"]
+ r2.vtysh_cmd("conf t\ninterface r2-eth1\nno ip ospf prefix-suppression")
+
+ step("Verify no R1 configuration of 'ip ospf prefix-suppression")
+ rc, _, _ = tgen.net["r1"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf prefix-suppression'", warn=False
+ )
+ assertmsg = (
+ "'ip ospf prefix-suppression' not applied, but present in R1 configuration"
+ )
+ assert rc, assertmsg
+
+ step("Verify that ospf-prefix suppression is not applied to the R1 interface")
+ r1_eth1_without_prefix_suppression = {
+ "interfaces": {
+ "r1-eth1": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.1.2.1",
+ "ospfIfType": "Broadcast",
+ "networkType": "NBMA",
+ "prefixSuppression": False,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip ospf interface r1-eth1 json",
+ r1_eth1_without_prefix_suppression,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "Prefix suppression on interface r1-eth1"
+ assert result is None, assertmsg
+
+ step("Verify that 10.1.2.0/24 route is now installed on R3")
+ input_dict = {
+ "10.1.2.0/24": [
+ {
+ "prefix": "10.1.2.0/24",
+ "prefixLen": 24,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": "10.1.5.2",
+ "interfaceName": "r3-eth0",
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.2.0/24 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.2.0/24 not installed on router r3"
+ assert result is None, assertmsg
+
+
+def test_p2p_suppression():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("Skipped because of router(s) failure")
+
+ step(
+ "Configure R1 interface r1-eth2 with prefix suppression with interface address"
+ )
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf t\ninterface r1-eth2\nip ospf prefix-suppression 10.1.3.1")
+
+ step(
+ "Configure R2 interface r2-eth1 with prefix suppression with interface address"
+ )
+ r2 = tgen.gears["r2"]
+ r2.vtysh_cmd("conf t\ninterface r2-eth2\nip ospf prefix-suppression 10.1.3.2")
+
+ step("Verify the R1 configuration of 'ip ospf prefix-suppression 10.1.3.1'")
+ prefix_suppression_cfg = (
+ tgen.net["r1"]
+ .cmd(
+ 'vtysh -c "show running ospfd" | grep "^ ip ospf prefix-suppression 10.1.3.1"'
+ )
+ .rstrip()
+ )
+ assertmsg = "'ip ospf prefix-suppression 10.1.3.1' applied, but not present in configuration"
+ assert prefix_suppression_cfg == " ip ospf prefix-suppression 10.1.3.1", assertmsg
+
+ step("Verify that ospf-prefix suppression is applied to the R1 interface")
+ r1_eth2_with_prefix_suppression = {
+ "interfaces": {
+ "r1-eth2": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.1.3.1",
+ "ospfIfType": "Broadcast",
+ "networkType": "POINTOPOINT",
+ "prefixSuppression": True,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip ospf interface r1-eth2 json",
+ r1_eth2_with_prefix_suppression,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "R1 OSPF interface r1-eth2 doesn't have prefix-suppression enabled"
+ assert result is None, assertmsg
+
+ step("Verify the OSPF prefix is not advertised and not present on r3")
+ r3 = tgen.gears["r3"]
+ input_dict = {}
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.3.0/24 json", input_dict, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.3.0/24 installed on router r3"
+ assert result is None, assertmsg
+
+ step(
+ "Remove R1 interface r1-eth2 prefix-suppression configuration using interface address"
+ )
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf t\ninterface r1-eth2\nno ip ospf prefix-suppression 10.1.3.1")
+
+ step(
+ "Remove R2 interface r2-eth2 prefix-suppression configuration using interface address"
+ )
+ r2 = tgen.gears["r2"]
+ r2.vtysh_cmd("conf t\ninterface r2-eth2\nno ip ospf prefix-suppression 10.1.3.2")
+
+ step("Verify no R1 configuration of 'ip ospf prefix-suppression")
+ rc, _, _ = tgen.net["r1"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf prefix-suppression 10.1.3.1'", warn=False
+ )
+ assertmsg = "'ip ospf prefix-suppressio 10.1.3.1' not applied, but present in R1 configuration"
+ assert rc, assertmsg
+
+ step("Verify that ospf-prefix suppression is not applied to the R1 interface")
+ r1_eth2_without_prefix_suppression = {
+ "interfaces": {
+ "r1-eth2": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.1.3.1",
+ "ospfIfType": "Broadcast",
+ "networkType": "POINTOPOINT",
+ "prefixSuppression": False,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip ospf interface r1-eth2 json",
+ r1_eth2_without_prefix_suppression,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "Prefix suppression on interface r1-eth2"
+ assert result is None, assertmsg
+
+ step("Verify that 10.1.3.0/24 route is now installed on R3")
+ input_dict = {
+ "10.1.3.0/24": [
+ {
+ "prefix": "10.1.3.0/24",
+ "prefixLen": 24,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": "10.1.5.2",
+ "interfaceName": "r3-eth0",
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.3.0/24 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.3.0/24 not installed on router r3"
+ assert result is None, assertmsg
+
+
+def test_p2mp_suppression():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("Skipped because of router(s) failure")
+
+ step("Configure R1 interface r1-eth3 with prefix suppression")
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf t\ninterface r1-eth3\nip ospf prefix-suppression")
+
+ step("Configure R2 interface r2-eth3 with prefix suppression")
+ r2 = tgen.gears["r2"]
+ r2.vtysh_cmd("conf t\ninterface r2-eth3\nip ospf prefix-suppression")
+
+ step("Verify that ospf-prefix suppression is applied to the R1 interface")
+ r1_eth3_with_prefix_suppression = {
+ "interfaces": {
+ "r1-eth3": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.1.4.1",
+ "ospfIfType": "Broadcast",
+ "networkType": "POINTOMULTIPOINT",
+ "prefixSuppression": True,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip ospf interface r1-eth3 json",
+ r1_eth3_with_prefix_suppression,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "R1 OSPF interface r1-eth3 doesn't have prefix-suppression enabled"
+ assert result is None, assertmsg
+
+ step("Verify the OSPF P2MP prefixes are not advertised and not present on r3")
+ r3 = tgen.gears["r3"]
+ input_dict = {}
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.4.1/32 json", input_dict, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.4.1/32 installed on router r3"
+ assert result is None, assertmsg
+
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.4.2/32 json", input_dict, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.4.2/32 installed on router r3"
+ assert result is None, assertmsg
+
+ step("Remove R1 interface r1-eth3 prefix-suppression configuration")
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf t\ninterface r1-eth3\nno ip ospf prefix-suppression")
+
+ step("Remove R2 interface r2-eth3 prefix-suppression configuration")
+ r2 = tgen.gears["r2"]
+ r2.vtysh_cmd("conf t\ninterface r2-eth3\nno ip ospf prefix-suppression")
+
+ step("Verify no R1 configuration of 'ip ospf prefix-suppression")
+ rc, _, _ = tgen.net["r1"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf prefix-suppression'", warn=False
+ )
+ assertmsg = (
+ "'ip ospf prefix-suppression' not applied, but present in R1 configuration"
+ )
+ assert rc, assertmsg
+
+ step("Verify that ospf-prefix suppression is not applied to the R1 interface")
+ r1_eth3_without_prefix_suppression = {
+ "interfaces": {
+ "r1-eth3": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.1.4.1",
+ "ospfIfType": "Broadcast",
+ "networkType": "POINTOMULTIPOINT",
+ "prefixSuppression": False,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip ospf interface r1-eth3 json",
+ r1_eth3_without_prefix_suppression,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "Prefix suppression on interface r1-eth3"
+ assert result is None, assertmsg
+
+ step("Verify that 10.1.4.1/32 route is now installed on R3")
+ input_dict = {
+ "10.1.4.1/32": [
+ {
+ "prefix": "10.1.4.1/32",
+ "prefixLen": 32,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": "10.1.5.2",
+ "interfaceName": "r3-eth0",
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.4.1/32 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.4.1/32 not installed on router r3"
+ assert result is None, assertmsg
+
+ step("Verify that 10.1.4.2/32 route is now installed on R3")
+ input_dict = {
+ "10.1.4.2/32": [
+ {
+ "prefix": "10.1.4.2/32",
+ "prefixLen": 32,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": "10.1.5.2",
+ "interfaceName": "r3-eth0",
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 10.1.4.2/32 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "10.1.4.2/32 not installed on router r3"
+ 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/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..21ae143
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/test_ospf_sr_te_topo1.py
@@ -0,0 +1,653 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_sr_te_topo1.py
+#
+# Copyright (c) 2021 by
+# Volta Networks
+#
+
+"""
+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..936b438
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/test_ospf_sr_topo1.py
@@ -0,0 +1,669 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_sr_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..d5583ac
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_suppres_fa.py
+# Carles Kishimoto
+#
+
+"""
+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\narea {} nssa\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..c47789a
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/r1/zebra.conf
@@ -0,0 +1,22 @@
+!
+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
+ max-bw 10e+10
+ 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..a9771f7
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/r2/zebra.conf
@@ -0,0 +1,34 @@
+!
+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
+ max-bw 10e+10
+ 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..8b2413a
--- /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":"10.0.0.1",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.0.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.1.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.4.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.5.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "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..625b57d
--- /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":"10.0.0.1",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.0.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.4.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.5.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "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..4cfec0f
--- /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":"10.0.0.1",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":"10.0.0.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.4.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "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..e8e24d9
--- /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":"10.0.0.1",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.0.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.3.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.4.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "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..4713cc0
--- /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":"10.0.0.1",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.0.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.1.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.1.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5007,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5006,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.3.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.4.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "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..aaac07b
--- /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":"10.0.0.1",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.0.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.1.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.1.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5007,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5006,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.3.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.4.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":20000,
+ "jitter":10000
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.4.2",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "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..56ed1f1
--- /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":"10.0.0.1",
+ "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":99999997952,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.0.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.1.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.1.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5007,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5006,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":"10.0.3.1",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":"10.0.3.2",
+ "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":1250000,
+ "max-resv-link-bandwidth":1250000,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":1250000
+ },
+ {
+ "class-type-1":1250000
+ },
+ {
+ "class-type-2":1250000
+ },
+ {
+ "class-type-3":1250000
+ },
+ {
+ "class-type-4":1250000
+ },
+ {
+ "class-type-5":1250000
+ },
+ {
+ "class-type-6":1250000
+ },
+ {
+ "class-type-7":1250000
+ }
+ ]
+ },
+ "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..c8533cf
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py
@@ -0,0 +1,282 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_te_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by Orange
+# Author: Olivier Dugeon <olivier.dugeon@orange.com>
+#
+
+"""
+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..f939f3f
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/test_ospf_tilfa_topo1.py
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_tilfa_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..a079f56
--- /dev/null
+++ b/tests/topotests/ospf_topo1/test_ospf_topo1.py
@@ -0,0 +1,585 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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_topo2/__init__.py b/tests/topotests/ospf_topo2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ospf_topo2/__init__.py
diff --git a/tests/topotests/ospf_topo2/r1/frr.conf b/tests/topotests/ospf_topo2/r1/frr.conf
new file mode 100644
index 0000000..9bc3361
--- /dev/null
+++ b/tests/topotests/ospf_topo2/r1/frr.conf
@@ -0,0 +1,61 @@
+frr defaults traditional
+hostname r1
+log syslog informational
+service integrated-vtysh-config
+!
+ip router-id 192.0.2.1
+!
+interface eth1
+ ip address 192.0.2.1/32
+ ip ospf area 0.0.0.0
+ ip ospf dead-interval minimal hello-multiplier 4
+ ip ospf network point-to-point
+ ipv6 address 2001:db8::1/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 dead-interval 4
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 network point-to-point
+exit
+!
+interface eth2
+ ip address 192.0.2.1/32
+ ip ospf area 0.0.0.0
+ ip ospf dead-interval minimal hello-multiplier 4
+ ip ospf network point-to-point
+ ipv6 address 2001:db8::1/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 dead-interval 4
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 network point-to-point
+exit
+!
+interface eth3
+ ip address 192.0.2.1/32
+ ip ospf area 0.0.0.0
+ ip ospf dead-interval minimal hello-multiplier 4
+ ip ospf network point-to-point
+ ipv6 address 2001:db8::1/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 dead-interval 4
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 network point-to-point
+exit
+!
+interface lo
+ ip address 192.0.2.1/32
+ ip ospf area 0.0.0.0
+ ip ospf passive
+ ipv6 address 2001:db8::1/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 passive
+exit
+!
+router ospf
+ log-adjacency-changes
+exit
+!
+router ospf6
+ log-adjacency-changes
+exit
+!
+end \ No newline at end of file
diff --git a/tests/topotests/ospf_topo2/r2/frr.conf b/tests/topotests/ospf_topo2/r2/frr.conf
new file mode 100644
index 0000000..d2ffb73
--- /dev/null
+++ b/tests/topotests/ospf_topo2/r2/frr.conf
@@ -0,0 +1,61 @@
+frr defaults traditional
+hostname r2
+log syslog informational
+service integrated-vtysh-config
+!
+ip router-id 192.0.2.2
+!
+interface eth1
+ ip address 192.0.2.2/32
+ ip ospf area 0.0.0.0
+ ip ospf dead-interval minimal hello-multiplier 4
+ ip ospf network point-to-point
+ ipv6 address 2001:db8::2/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 dead-interval 4
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 network point-to-point
+exit
+!
+interface eth2
+ ip address 192.0.2.2/32
+ ip ospf area 0.0.0.0
+ ip ospf dead-interval minimal hello-multiplier 4
+ ip ospf network point-to-point
+ ipv6 address 2001:db8::2/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 dead-interval 4
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 network point-to-point
+exit
+!
+interface eth3
+ ip address 192.0.2.2/32
+ ip ospf area 0.0.0.0
+ ip ospf dead-interval minimal hello-multiplier 4
+ ip ospf network point-to-point
+ ipv6 address 2001:db8::2/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 dead-interval 4
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 network point-to-point
+exit
+!
+interface lo
+ ip address 192.0.2.2/32
+ ip ospf area 0.0.0.0
+ ip ospf passive
+ ipv6 address 2001:db8::2/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 passive
+exit
+!
+router ospf
+ log-adjacency-changes
+exit
+!
+router ospf6
+ log-adjacency-changes
+exit
+!
+end \ No newline at end of file
diff --git a/tests/topotests/ospf_topo2/r3/frr.conf b/tests/topotests/ospf_topo2/r3/frr.conf
new file mode 100644
index 0000000..e87b897
--- /dev/null
+++ b/tests/topotests/ospf_topo2/r3/frr.conf
@@ -0,0 +1,61 @@
+frr defaults traditional
+hostname r3
+log syslog informational
+service integrated-vtysh-config
+!
+ip router-id 192.0.2.3
+!
+interface eth1
+ ip address 192.0.2.3/32
+ ip ospf area 0.0.0.0
+ ip ospf dead-interval minimal hello-multiplier 4
+ ip ospf network point-to-point
+ ipv6 address 2001:db8::3/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 dead-interval 4
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 network point-to-point
+exit
+!
+interface eth2
+ ip address 192.0.2.3/32
+ ip ospf area 0.0.0.0
+ ip ospf dead-interval minimal hello-multiplier 4
+ ip ospf network point-to-point
+ ipv6 address 2001:db8::3/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 dead-interval 4
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 network point-to-point
+exit
+!
+interface eth3
+ ip address 192.0.2.3/32
+ ip ospf area 0.0.0.0
+ ip ospf dead-interval minimal hello-multiplier 4
+ ip ospf network point-to-point
+ ipv6 address 2001:db8::3/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 dead-interval 4
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 network point-to-point
+exit
+!
+interface lo
+ ip address 192.0.2.3/32
+ ip ospf area 0.0.0.0
+ ip ospf passive
+ ipv6 address 2001:db8::3/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 passive
+exit
+!
+router ospf
+ log-adjacency-changes
+exit
+!
+router ospf6
+ log-adjacency-changes
+exit
+!
+end \ No newline at end of file
diff --git a/tests/topotests/ospf_topo2/r4/frr.conf b/tests/topotests/ospf_topo2/r4/frr.conf
new file mode 100644
index 0000000..4e33d75
--- /dev/null
+++ b/tests/topotests/ospf_topo2/r4/frr.conf
@@ -0,0 +1,61 @@
+frr defaults traditional
+hostname r4
+log syslog informational
+service integrated-vtysh-config
+!
+ip router-id 192.0.2.4
+!
+interface eth1
+ ip address 192.0.2.4/32
+ ip ospf area 0.0.0.0
+ ip ospf dead-interval minimal hello-multiplier 4
+ ip ospf network point-to-point
+ ipv6 address 2001:db8::4/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 dead-interval 4
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 network point-to-point
+exit
+!
+interface eth2
+ ip address 192.0.2.4/32
+ ip ospf area 0.0.0.0
+ ip ospf dead-interval minimal hello-multiplier 4
+ ip ospf network point-to-point
+ ipv6 address 2001:db8::4/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 dead-interval 4
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 network point-to-point
+exit
+!
+interface eth3
+ ip address 192.0.2.4/32
+ ip ospf area 0.0.0.0
+ ip ospf dead-interval minimal hello-multiplier 4
+ ip ospf network point-to-point
+ ipv6 address 2001:db8::4/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 dead-interval 4
+ ipv6 ospf6 hello-interval 1
+ ipv6 ospf6 network point-to-point
+exit
+!
+interface lo
+ ip address 192.0.2.4/32
+ ip ospf area 0.0.0.0
+ ip ospf passive
+ ipv6 address 2001:db8::4/128
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 passive
+exit
+!
+router ospf
+ log-adjacency-changes
+exit
+!
+router ospf6
+ log-adjacency-changes
+exit
+!
+end \ No newline at end of file
diff --git a/tests/topotests/ospf_topo2/test_ospf_topo2.dot b/tests/topotests/ospf_topo2/test_ospf_topo2.dot
new file mode 100644
index 0000000..e35afbb
--- /dev/null
+++ b/tests/topotests/ospf_topo2/test_ospf_topo2.dot
@@ -0,0 +1,44 @@
+graph template {
+ label="ospf_topo2";
+ splines = "line"
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1\n192.0.2.1\n2001:db8::1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon,
+ label="r2\n\192.0.2.2\n2001:db8::2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ shape=doubleoctagon,
+ label="r3\n192.0.2.3\n2001:db8::3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r4 [
+ shape=doubleoctagon,
+ label="r4\n192.0.2.4\n2001:db8::4",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- r2 [label="eth1"];
+ r1 -- r2 [label="eth2"];
+
+ r2 -- r3 [label="eth3\neth1"];
+ r1 -- r4 [label="eth3\neth1"];
+
+ r4 -- r3 [label="eth2"];
+ r4 -- r3 [label="eth3"];
+
+ # Group r1 and r2 above, r3 and r4 below
+ { rank=min; r1; r2; }
+ { rank=max; r3; r4; }
+}
diff --git a/tests/topotests/ospf_topo2/test_ospf_topo2.png b/tests/topotests/ospf_topo2/test_ospf_topo2.png
new file mode 100644
index 0000000..7eb0a1d
--- /dev/null
+++ b/tests/topotests/ospf_topo2/test_ospf_topo2.png
Binary files differ
diff --git a/tests/topotests/ospf_topo2/test_ospf_topo2.py b/tests/topotests/ospf_topo2/test_ospf_topo2.py
new file mode 100644
index 0000000..8be06e4
--- /dev/null
+++ b/tests/topotests/ospf_topo2/test_ospf_topo2.py
@@ -0,0 +1,317 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: ISC
+#
+# test_ospf_topo2.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+test_ospf_topo2.py: Test correct route removal.
+
+Proofs the following issue:
+https://github.com/FRRouting/frr/issues/14488
+
+"""
+
+import ipaddress
+import json
+import pytest
+import sys
+import time
+
+from lib.topogen import Topogen
+
+
+pytestmark = [
+ pytest.mark.ospf6d,
+ pytest.mark.ospfd,
+]
+
+
+def build_topo(tgen):
+ """Build the topology used by all tests below."""
+
+ # Create 4 routers
+ r1 = tgen.add_router("r1")
+ r2 = tgen.add_router("r2")
+ r3 = tgen.add_router("r3")
+ r4 = tgen.add_router("r4")
+
+ # The r1/r2 and r3/r4 router pairs have two connections each
+ tgen.add_link(r1, r2, ifname1="eth1", ifname2="eth1")
+ tgen.add_link(r1, r2, ifname1="eth2", ifname2="eth2")
+ tgen.add_link(r3, r4, ifname1="eth2", ifname2="eth2")
+ tgen.add_link(r3, r4, ifname1="eth3", ifname2="eth3")
+
+ # The r1/r4 and r2/r3 router pairs have one connection each
+ tgen.add_link(r1, r4, ifname1="eth3", ifname2="eth1")
+ tgen.add_link(r2, r3, ifname1="eth3", ifname2="eth1")
+
+
+@pytest.fixture(scope="function")
+def tgen(request):
+ """Setup/Teardown the environment and provide tgen argument to tests.
+
+ Do this once per function as some of the tests will leave the router
+ in an unclean state.
+
+ """
+
+ tgen = Topogen(build_topo, request.module.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ router.load_frr_config("frr.conf")
+
+ tgen.start_router()
+
+ yield tgen
+
+ tgen.stop_topology()
+
+
+def ospf_neighbors(router, ip_version):
+ """List the OSPF neighbors for the given router and IP version."""
+
+ if ip_version == 4:
+ cmd = "show ip ospf neighbor json"
+ else:
+ cmd = "show ipv6 ospf neighbor json"
+
+ output = router.vtysh_cmd(cmd)
+
+ if ip_version == 4:
+ return [v for n in json.loads(output)["neighbors"].values() for v in n]
+ else:
+ return json.loads(output)["neighbors"]
+
+
+def ospf_neighbor_uptime(router, interface, ip_version):
+ """Uptime of the neighbor with the given interface name in seconds."""
+
+ for neighbor in ospf_neighbors(router, ip_version):
+ if ip_version == 4:
+ if not neighbor["ifaceName"].startswith("{}:".format(interface)):
+ continue
+
+ return neighbor["upTimeInMsec"] / 1000
+ else:
+ if neighbor["interfaceName"] != interface:
+ continue
+
+ h, m, s = [int(d) for d in neighbor["duration"].split(":")]
+ return h * 3600 + m * 60 + s
+
+ raise KeyError(
+ "No IPv{} neighbor with interface name {} on {}".format(
+ ip_version, interface, router.name
+ )
+ )
+
+
+def ospf_routes(router, prefix):
+ """List the OSPF routes for the given router and prefix."""
+
+ if ipaddress.ip_interface(prefix).ip.version == 4:
+ cmd = "show ip route {} json"
+ else:
+ cmd = "show ipv6 route {} json"
+
+ output = router.vtysh_cmd(cmd.format(prefix))
+ return json.loads(output)[prefix]
+
+
+def ospf_nexthops(router, prefix, protocol):
+ """List the OSPF nexthops for the given prefix."""
+
+ for route in ospf_routes(router, prefix):
+ if route["protocol"] != protocol:
+ continue
+
+ for nexthop in route["nexthops"]:
+ yield nexthop
+
+
+def ospf_directly_connected_interfaces(router, ip_version):
+ """The names of the directly connected interfaces, as discovered
+ through the OSPF nexthops.
+
+ """
+
+ if ip_version == 4:
+ prefix = "192.0.2.{}/32".format(router.name.strip("r"))
+ else:
+ prefix = "fe80::/64"
+
+ hops = ospf_nexthops(router, prefix, protocol="connected")
+ return sorted([n["interfaceName"] for n in hops if n["directlyConnected"]])
+
+
+def wait_for_ospf(router, ip_version, neighbors, timeout=60):
+ """Wait until the router has the given number of neighbors that are
+ fully converged.
+
+ Note that this checks for the exact number of neighbors, so if one neighbor
+ is requested and three are converged, the wait continues.
+
+ """
+
+ until = time.monotonic() + timeout
+
+ if ip_version == 4:
+ filter = {"converged": "Full"}
+ else:
+ filter = {"state": "Full"}
+
+ def is_match(neighbor):
+ for k, v in filter.items():
+ if neighbor[k] != v:
+ return False
+
+ return True
+
+ while time.monotonic() < until:
+ found = sum(1 for n in ospf_neighbors(router, ip_version) if is_match(n))
+
+ if neighbors == found:
+ return
+
+ raise TimeoutError(
+ "Waited over {}s for {} neighbors to reach {}".format(
+ timeout, neighbors, filter
+ )
+ )
+
+
+@pytest.mark.parametrize("ip_version", [4, 6])
+def test_interface_up(tgen, ip_version):
+ """Verify the initial routing table, before any changes."""
+
+ # Wait for the routers to be ready
+ routers = {id: tgen.gears[id] for id in ("r1", "r2", "r3", "r4")}
+
+ for router in routers.values():
+ wait_for_ospf(router, ip_version=ip_version, neighbors=3)
+
+ # Verify that the link-local routes are correct
+ for router in routers.values():
+ connected = ospf_directly_connected_interfaces(router, ip_version)
+
+ if ip_version == 4:
+ expected = ["eth1", "eth2", "eth3", "lo"]
+ else:
+ expected = ["eth1", "eth2", "eth3"]
+
+ assert (
+ connected == expected
+ ), "Expected all interfaces to be connected on {}".format(router.name)
+
+
+@pytest.mark.parametrize("ip_version", [4, 6])
+def test_interface_down(tgen, ip_version):
+ """Verify the routing table after taking interfaces down."""
+
+ # Wait for the routers to be ready
+ routers = {id: tgen.gears[id] for id in ("r1", "r2", "r3", "r4")}
+
+ for id, router in routers.items():
+ wait_for_ospf(router, ip_version=ip_version, neighbors=3)
+
+ # Keep track of the uptime of the eth3 neighbor
+ uptime = ospf_neighbor_uptime(routers["r1"], "eth3", ip_version)
+ before = time.monotonic()
+
+ # Take the links between r1 and r2 down
+ routers["r1"].cmd_raises("ip link set down dev eth1")
+ routers["r1"].cmd_raises("ip link set down dev eth2")
+
+ # Wait for OSPF to converge
+ wait_for_ospf(routers["r1"], ip_version=ip_version, neighbors=1)
+
+ # The uptime of the unaffected eth3 neighbor should be monotonic
+ new_uptime = ospf_neighbor_uptime(routers["r1"], "eth3", ip_version)
+ took = round(time.monotonic() - before, 3)
+
+ # IPv6 has a resolution of 1s, for IPv4 some slack is necesssary.
+ if ip_version == 4:
+ offset = 0.25
+ else:
+ offset = 1
+
+ assert (
+ new_uptime + offset >= uptime + took
+ ), "The eth3 neighbor uptime must not decrease"
+
+ # We should only find eth3 once OSPF has converged
+ connected = ospf_directly_connected_interfaces(routers["r1"], ip_version)
+
+ if ip_version == 4:
+ expected = ["eth3", "lo"]
+ else:
+ expected = ["eth3"]
+
+ assert connected == expected, "Expected only eth1 and eth2 to be disconnected"
+
+
+@pytest.mark.parametrize("ip_version", [4, 6])
+def test_interface_flap(tgen, ip_version):
+ """Verify the routing table after enabling an interface that was down."""
+
+ # Wait for the routers to be ready
+ routers = {id: tgen.gears[id] for id in ("r1", "r2", "r3", "r4")}
+
+ for id, router in routers.items():
+ wait_for_ospf(router, ip_version=ip_version, neighbors=3)
+
+ # Keep track of the uptime of the eth3 neighbor
+ uptime = ospf_neighbor_uptime(routers["r1"], "eth3", ip_version)
+ before = time.monotonic()
+
+ # Take the links between r1 and r2 down
+ routers["r1"].cmd_raises("ip link set down dev eth1")
+ routers["r2"].cmd_raises("ip link set down dev eth2")
+
+ # Wait for OSPF to converge
+ wait_for_ospf(routers["r1"], ip_version=ip_version, neighbors=1)
+
+ # Take the links between r1 and r2 up
+ routers["r1"].cmd_raises("ip link set up dev eth1")
+ routers["r2"].cmd_raises("ip link set up dev eth2")
+
+ # Wait for OSPF to converge
+ wait_for_ospf(routers["r1"], ip_version=ip_version, neighbors=3)
+
+ # The uptime of the unaffected eth3 neighbor should be monotonic
+ new_uptime = ospf_neighbor_uptime(routers["r1"], "eth3", ip_version)
+ took = round(time.monotonic() - before, 3)
+
+ # IPv6 has a resolution of 1s, for IPv4 some slack is necesssary.
+ if ip_version == 4:
+ offset = 0.25
+ else:
+ offset = 1
+
+ assert (
+ new_uptime + offset >= uptime + took
+ ), "The eth3 neighbor uptime must not decrease"
+
+ # We should find all interfaces again
+ connected = ospf_directly_connected_interfaces(routers["r1"], ip_version)
+
+ if ip_version == 4:
+ expected = ["eth1", "eth2", "eth3", "lo"]
+ else:
+ expected = ["eth1", "eth2", "eth3"]
+
+ assert connected == expected, "Expected all interfaces to be connected"
+
+
+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..cfd9d85
--- /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": " ", "directlyAttachedTo": "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..e56b872
--- /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": " ", "directlyAttachedTo": "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..d07f5dc
--- /dev/null
+++ b/tests/topotests/ospf_unnumbered/test_ospf_unnumbered.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_unnumbered.py
+#
+# Copyright (c) 2019 by
+# Cumulus Networks, Inc
+# Donald Sharp
+#
+
+"""
+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..ab23744
--- /dev/null
+++ b/tests/topotests/ospfapi/ctester.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: MIT
+#
+# January 17 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2022, LabN Consulting, L.L.C.
+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..7a7ea85
--- /dev/null
+++ b/tests/topotests/ospfapi/test_ospf_clientapi.py
@@ -0,0 +1,1586 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (c) 2021-2022, LabN Consulting, L.L.C.
+#
+
+"""
+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
+from functools import partial
+
+import pytest
+from lib.common_config import (
+ kill_router_daemons,
+ retry,
+ run_frr_cmd,
+ shutdown_bringup_interface,
+ start_router_daemons,
+ step,
+)
+from lib.micronet import Timeout, comm_error
+from lib.topogen import Topogen, TopoRouter
+from lib.topotest import interface_set_status, json_cmp
+
+# 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]
+
+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", # seq = 80000001
+ "wait, 5",
+ "add,10,1.2.3.4,231,1,feedaceebeef", # seq = 80000002
+ "wait, 5",
+ ]
+ )
+ add_input_dict = {
+ "areas": {
+ "1.2.3.4": {
+ "areaLocalOpaqueLsa": [
+ {
+ "lsId": "231.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000002",
+ "checksum": "cd26",
+ },
+ ],
+ "areaLocalOpaqueLsaCount": 1,
+ },
+ },
+ }
+ step("Wait for the Opaque LSA to be distributed")
+ 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
+
+ # Verify the OLD LSA is still there unchanged on R2
+ assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is 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"])
+
+ # This will start off with sequence num 80000001
+ # But should advance to 80000003 when we reestablish with r2
+ p = r1.popen(
+ [
+ apibin,
+ "-v",
+ "add,10,1.2.3.4,231,1,feedaceecafebeef", # seq=80000001
+ "wait, 5",
+ ]
+ )
+
+ # verify the old value on r2 doesn't change yet
+ time.sleep(2)
+ assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is None
+
+ json_cmd = "show ip ospf da opaque-area json"
+ new_add_input_dict = {
+ "areaLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "231.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "checksum": "b07a",
+ "length": 28,
+ "opaqueDataLength": 8,
+ },
+ ],
+ },
+ },
+ }
+ # verify new value with initial seq number on r1
+ assert verify_ospf_database(tgen, r1, new_add_input_dict, json_cmd) is None
+
+ 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")
+
+ # Update the expected value to sequence number rev and new checksum
+ update_dict = new_add_input_dict["areaLocalOpaqueLsa"]["areas"]["1.2.3.4"][0]
+ update_dict["lsaSeqNumber"] = "80000003"
+ update_dict["checksum"] = "cb27"
+
+ # should settle on the same value now.
+ assert verify_ospf_database(tgen, r1, new_add_input_dict, json_cmd) is None
+ assert verify_ospf_database(tgen, r2, new_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"])
+
+ 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": "80000003",
+ "checksum": "cb27",
+ "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)
+
+
+def _test_opaque_interface_disable(tgen, apibin):
+ "Test disabling opaque capability on an interface"
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ tc_name = "opaque_interface_disable"
+
+ p = None
+ pread = None
+ # Log to our stdin, stderr
+ pout = open(os.path.join(r1.net.logdir, "r1/intf-disable.log"), "a+")
+ try:
+ # STEP 1 in test_ospf_opaque_interface_disable and STEP 56 in CI tests
+ step("Disable OSPF opaque LSA Copability on r1's interface to r2")
+ r1.vtysh_multicmd("conf t\ninterface r1-eth0\nno ip ospf capability opaque")
+ time.sleep(15)
+
+ # STEP 2 in test_ospf_opaque_interface_disable and STEP 57 in CI tests
+ step("Verify the r1 configuration of 'no ip ospf capability opaque'")
+ no_capability_opaque_cfg = (
+ tgen.net["r1"]
+ .cmd(
+ 'vtysh -c "show running ospfd" | grep "^ no ip ospf capability opaque"'
+ )
+ .rstrip()
+ )
+ assertmsg = (
+ "'no ip ospf capability opaque' applied, but not present in configuration"
+ )
+ assert no_capability_opaque_cfg == " no ip ospf capability opaque", assertmsg
+
+ # STEP 3 in test_ospf_opaque_interface_disable and STEP 58 in CI tests
+ step("Verify the ospf opaque option is not applied to the r1 interface")
+ r1_interface_without_opaque = {
+ "interfaces": {
+ "r1-eth0": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.0.1.1",
+ "ospfIfType": "Broadcast",
+ "opaqueCapable": False,
+ }
+ }
+ }
+ r1_interface_with_opaque = {
+ "interfaces": {
+ "r1-eth0": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.0.1.1",
+ "ospfIfType": "Broadcast",
+ "opaqueCapable": True,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_without_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF interface doesn't have opaque capability disabled"
+ assert result is None, assertmsg
+
+ r1_neighbor_without_opaque = {
+ "neighbors": {
+ "2.0.0.0": [
+ {
+ "optionsList": "*|-|-|-|-|-|E|-",
+ }
+ ]
+ }
+ }
+ r2_neighbor_without_opaque = {
+ "neighbors": {
+ "1.0.0.0": [
+ {
+ "optionsList": "*|-|-|-|-|-|E|-",
+ }
+ ]
+ }
+ }
+ # STEP 4 in test_ospf_opaque_interface_disable and STEP 59 in CI tests
+ step("Verify that the r1 neighbor options don't include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF neighbor has opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 5 in test_ospf_opaque_interface_disable and STEP 60 in CI tests
+ step("Verify that the r1 neighbor options don't include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF neighbor has opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 6 in test_ospf_opaque_interface_disable and STEP 61 in CI tests
+ step(
+ "Verify no r2 configuration of 'no ip ospf capability opaque' in r2 configuration"
+ )
+ rc, _, _ = tgen.net["r2"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False
+ )
+ assertmsg = "'no ip ospf capability opaque' not applied, but not present in r2 configuration"
+ assert rc, assertmsg
+
+ # STEP 7 in test_ospf_opaque_interface_disable and STEP 62 in CI tests
+ step("Verify the ospf opaque option is applied to the r2 interface")
+ r2_interface_without_opaque = {
+ "interfaces": {
+ "r2-eth0": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.0.1.2",
+ "ospfIfType": "Broadcast",
+ "opaqueCapable": False,
+ }
+ }
+ }
+ r2_interface_with_opaque = {
+ "interfaces": {
+ "r2-eth0": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.0.1.2",
+ "ospfIfType": "Broadcast",
+ "opaqueCapable": True,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF interface has opaque capability disabled"
+ assert result is None, assertmsg
+
+ # STEP 8 in test_ospf_opaque_interface_disable and STEP 63 in CI tests
+ step("Install opaque LSAs on r1")
+ 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,feedaceedeadbeef",
+ "add,10,1.2.3.4,231,1,feedaceecafebeef",
+ "add,11,232,1,feedaceebaddbeef",
+ "wait,20",
+ ]
+ )
+ opaque_LSAs_in_database = {
+ "areas": {
+ "1.2.3.4": {
+ "linkLocalOpaqueLsa": [
+ {
+ "lsId": "230.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ },
+ ],
+ "linkLocalOpaqueLsaCount": 1,
+ "areaLocalOpaqueLsa": [
+ {
+ "lsId": "231.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ },
+ ],
+ "areaLocalOpaqueLsaCount": 1,
+ },
+ },
+ "asExternalOpaqueLsa": [
+ {
+ "lsId": "232.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ },
+ ],
+ "asExternalOpaqueLsaCount": 1,
+ }
+ opaque_area_empty_database = {
+ "routerId":"2.0.0.0",
+ "areaLocalOpaqueLsa":{
+ "areas":{
+ "1.2.3.4":[
+ ]
+ }
+ }
+ }
+
+ # STEP 9 in test_ospf_opaque_interface_disable and STEP 64 in CI tests
+ step("Check that LSAs are added on r1")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF database doesn't contain opaque LSAs"
+ assert result is None, assertmsg
+
+ # STEP 10 in test_ospf_opaque_interface_disable and STEP 65 in CI tests
+ step("Check that LSAs are not added on r2")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf database opaque-area json",
+ opaque_area_empty_database, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF area database contains opaque LSAs"
+ assert result is None, assertmsg
+
+ # STEP 11 in test_ospf_opaque_interface_disable and STEP 66 in CI tests
+ step("Enable OSPF opaque LSA Copability on r1's interface to r2")
+ r1.vtysh_multicmd("conf t\ninterface r1-eth0\nip ospf capability opaque")
+ time.sleep(15)
+
+ # STEP 12 in test_ospf_opaque_interface_disable and STEP 67 in CI tests
+ step("Verify no r1 configuration of 'no ip ospf capability opaque'")
+ rc, _, _ = tgen.net["r1"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False
+ )
+ assertmsg = "'no ip ospf capability opaque' not applied, but not present in r1 configuration"
+ assert rc, assertmsg
+
+ # STEP 13 in test_ospf_opaque_interface_disable and STEP 68 in CI tests
+ step("Verify the ospf opaque option is applied to the r1 interface")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF interface doesn't have opaque capability disabled"
+ assert result is None, assertmsg
+
+ r1_neighbor_with_opaque = {
+ "neighbors": {
+ "2.0.0.0": [
+ {
+ "optionsList": "*|O|-|-|-|-|E|-",
+ }
+ ]
+ }
+ }
+ r2_neighbor_with_opaque = {
+ "neighbors": {
+ "1.0.0.0": [
+ {
+ "optionsList": "*|O|-|-|-|-|E|-",
+ }
+ ]
+ }
+ }
+ # STEP 14 in test_ospf_opaque_interface_disable and STEP 69 in CI tests
+ step("Verify that the r1 neighbor options include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 15 in test_ospf_opaque_interface_disable and STEP 70 in CI tests
+ step("Verify that the r2 neighbor options include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 16 in test_ospf_opaque_interface_disable and STEP 71 in CI tests
+ step("Check that LSAs are now added to r2")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF database doesn't contains opaque LSAs"
+ assert result is None, assertmsg
+
+ # STEP 17 in test_ospf_opaque_interface_disable and STEP 72 in CI tests
+ step(
+ "Disable Opaque Capability on r2's interface to r1 using the interface address"
+ )
+ r2.vtysh_multicmd(
+ "conf t\ninterface r2-eth0\nno ip ospf capability opaque 10.0.1.2"
+ )
+
+ # STEP 18 in test_ospf_opaque_interface_disable and STEP 73 in CI tests
+ step("Clear the OSPF process on r2 to clear the OSPF LSDB")
+ r2.vtysh_multicmd("clear ip ospf process")
+ time.sleep(15)
+
+ # STEP 19 in test_ospf_opaque_interface_disable and STEP 74 in CI tests
+ step("Verify the r2 configuration of 'no ip ospf capability opaque 10.0.1.2'")
+ no_capability_opaque_cfg = (
+ tgen.net["r2"]
+ .cmd_nostatus(
+ 'vtysh -c "show running ospfd" | grep "^ no ip ospf capability opaque 10.0.1.2"'
+ )
+ .rstrip()
+ )
+ assertmsg = "'no ip ospf capability opaque 10.0.1.2' applied, but not present in configuration"
+ assert (
+ no_capability_opaque_cfg == " no ip ospf capability opaque 10.0.1.2"
+ ), assertmsg
+
+ # STEP 20 in test_ospf_opaque_interface_disable and STEP 75 in CI tests
+ step("Verify the ospf opaque option is not applied to the r2 interface")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_without_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF interface doesn't have opaque capability disabled"
+ assert result is None, assertmsg
+
+ # STEP 21 in test_ospf_opaque_interface_disable and STEP 76 in CI tests
+ step("Verify that the r1 neighbor options don't include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF neighbor has opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 22 in test_ospf_opaque_interface_disable and STEP 77 in CI tests
+ step("Verify that the r2 neighbor options don't include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF neighbor has opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 23 in test_ospf_opaque_interface_disable and STEP 78 in CI tests
+ step("Verify that r1 still has the opaque LSAs")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF database doesn't contain opaque LSAs"
+ assert result is None, assertmsg
+
+ # STEP 24 in test_ospf_opaque_interface_disable and STEP 79 in CI tests
+ step("Verify that r2 doesn't have the opaque LSAs")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf database opaque-area json",
+ opaque_area_empty_database, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF area database contains opaque LSAs"
+ assert result is None, assertmsg
+
+ # STEP 25 in test_ospf_opaque_interface_disable and STEP 80 in CI tests
+ step("Remove the 'no ip ospf capability opaque 10.0.1.2' config from r2 ")
+ r2.vtysh_multicmd(
+ "conf t\ninterface r2-eth0\nip ospf capability opaque 10.0.1.2"
+ )
+ time.sleep(15)
+
+ # STEP 26 in test_ospf_opaque_interface_disable and STEP 81 in CI tests
+ step("Verify the r2 removal of 'no ip ospf capability opaque 10.0.1.2'")
+ rc, _, _ = tgen.net["r2"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False
+ )
+ assertmsg = "'no ip ospf capability opaque' not applied, but not present in r2 configuration"
+ assert rc, assertmsg
+
+ # STEP 27 in test_ospf_opaque_interface_disable and STEP 82 in CI tests
+ step("Verify the ospf opaque option is applied to the r2 interface")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF interface doesn't have opaque capability disabled"
+ assert result is None, assertmsg
+
+ # STEP 28 in test_ospf_opaque_interface_disable and STEP 83 in CI tests
+ step("Verify that the r2 neighbor options include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 29 in test_ospf_opaque_interface_disable and STEP 84 in CI tests
+ step("Verify that the r1 neighbor options include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 30 in test_ospf_opaque_interface_disable and STEP 85 in CLI tests
+ step("Verify that r2 now has the opaque LSAs")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF database doesn't contain opaque LSAs"
+ assert result is None, assertmsg
+
+ 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_interface_disable(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_interface_disable(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..49c25ab
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py
@@ -0,0 +1,2718 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+ 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.
+
+ # 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 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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("{}".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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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": {
+ "summaryAddress": "2011::/16",
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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": {
+ "summaryAddress": "2011::/32",
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 1,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 4294967295,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 88888,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 1,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 4294967295,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 88888,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][3],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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]: {
+ "summaryAddress": SUMMARY["ipv6"][0],
+ "metricType": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "externalRouteCount": 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..58608e2
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py
@@ -0,0 +1,1414 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+)
+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.
+
+ # 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)
+
+ 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..0c1e3fa
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py
@@ -0,0 +1,472 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+ 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.
+
+ # 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)
+
+ 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, "Testcase 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, "Testcase 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, "Testcase 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..7c67732
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py
@@ -0,0 +1,383 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+ 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.
+
+ # 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 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, "Testcase 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, "Testcase 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, "Testcase 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..dc4ce88
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py
@@ -0,0 +1,158 @@
+#!/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,
+)
+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.
+
+ # 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)
+
+ 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..90548fb
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py
@@ -0,0 +1,583 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+ 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.
+
+ # 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 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)
+
+
+def test_ospfv3_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():
+ check_router_status(tgen)
+
+ 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"]["ipv6"]
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": ip_net,
+ "no_of_ip": 1,
+ "routeType": "Network",
+ "pathtype": "Inter-Area",
+ }
+ ]
+ }
+ }
+
+ dut = "r0"
+ 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)
+
+ 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)
+
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "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)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_ospf6_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ 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: {
+ "ospf6": {"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 R1 installs type 5 lsa in its database.")
+ step("Verify that route is calculated and installed in R1.")
+
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "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)
+
+ 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)
+
+
+# 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..069806a
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py
@@ -0,0 +1,1229 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+)
+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.
+
+ # 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)
+
+ 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, "Testcase 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..645dea8
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
@@ -0,0 +1,906 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+ 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.
+
+ # 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)
+
+ 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, "Testcase 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, "Testcase 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..7199f16
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
@@ -0,0 +1,1266 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+
+
+"""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,
+ 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.
+
+ # 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)
+
+ 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, "Testcase 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, "Testcase 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, "Testcase 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, "Testcase 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)
+
+ 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..a70f2cc
--- /dev/null
+++ b/tests/topotests/pbr_topo1/test_pbr_topo1.py
@@ -0,0 +1,435 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_pbr_topo1.py
+#
+# Copyright (c) 2020 by
+# Cumulus Networks, Inc.
+# Donald Sharp
+#
+# Copyright (c) 2023 LabN Consulting, L.L.C.
+#
+
+"""
+test_pbr_topo1.py: Testing PBR
+
+"""
+
+import os
+import sys
+import pytest
+import json
+import platform
+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
+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")
+
+
+#
+# router: r1
+# tag: "show pbr interface"
+# cmd: "show pbr interface json"
+# expfile: "{}/{}/pbr-interface.json".format(CWD, router.name)
+#
+def runit(router, tag, cmd, expfile):
+ logger.info(expfile)
+
+ # Read expected result from file
+ expected = json.loads(open(expfile).read())
+
+ # Actual output from router
+ test_func = partial(topotest.router_json_cmp, router, cmd, expected)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"{}" mismatches on {}'.format(tag, router.name)
+ if result is not None:
+ gather_pbr_data_on_error(router)
+ assert result is None, assertmsg
+
+
+def test_pbr_data():
+ "Test PBR"
+
+ 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:
+ runit(
+ router,
+ "show pbr interface",
+ "show pbr interface json",
+ "{}/{}/pbr-interface.json".format(CWD, router.name),
+ )
+
+ runit(
+ router,
+ "show pbr map",
+ "show pbr map json",
+ "{}/{}/pbr-map.json".format(CWD, router.name),
+ )
+
+ runit(
+ router,
+ "show pbr nexthop-groups",
+ "show pbr nexthop-groups json",
+ "{}/{}/pbr-nexthop-groups.json".format(CWD, router.name),
+ )
+
+
+########################################################################
+# Field test - START
+########################################################################
+
+#
+# New fields:
+# match ip-protocol (was only tcp|udp, now any value in /etc/protocols)
+# match pcp (0-7)
+# match vlan (1-4094)
+# match vlan (tagged|untagged|untagged-or-zero)
+#
+
+#
+# c: command
+# cDN: omit default destination IP address (special case)
+# tm: must-match pattern
+# tN: must Not match pattern
+#
+# Note we are searching amid a bunch of other rules, so these elements
+# should be unique.
+#
+ftest = [
+ {"c": "match ip-protocol icmp", "tm": r"IP protocol Match: 1$"},
+ {"c": "no match ip-protocol icmp", "tN": r"IP protocol Match:"},
+ {"c": "match pcp 6", "tm": r"PCP Match: 6$"},
+ {"c": "match pcp 0", "tm": r"PCP Match: 0$"},
+ {"c": "no match pcp 0", "tN": r"PCP Match:"},
+ {"c": "match vlan 33", "tm": r"VLAN ID Match: 33$"},
+ {"c": "no match vlan 33", "tN": r"VLAN ID Match:"},
+ {"c": "match vlan tagged", "tm": r"VLAN Flags Match: tagged$"},
+ {"c": "match vlan untagged", "tm": r"VLAN Flags Match: untagged$"},
+ {"c": "match vlan untagged-or-zero", "tm": r"VLAN Flags Match: untagged-or-zero$"},
+ {"c": "no match vlan tagged", "tN": r"VLAN Flags Match:"},
+ {"c": "match src-ip 37.49.22.0/24", "tm": r"SRC IP Match: 37.49.22.0/24$"},
+ {"c": "no match src-ip 37.49.22.0/24", "tN": r"SRC IP Match: 37.49.22.0/24$"},
+ {
+ "c": "match dst-ip 38.41.29.0/25",
+ "cDN": "foo",
+ "tm": r"DST IP Match: 38.41.29.0/25$",
+ },
+ {"c": "no match dst-ip 38.41.29.0/25", "tN": r"DST IP Match: 38.41.29.0/25$"},
+ {"c": "match src-port 117", "tm": r"SRC Port Match: 117$"},
+ {"c": "no match src-port 117", "tN": r"SRC Port Match: 117$"},
+ {"c": "match dst-port 119", "tm": r"DST Port Match: 119$"},
+ {"c": "no match dst-port 119", "tN": r"DST Port Match: 119$"},
+ {"c": "match dscp cs3", "tm": r"DSCP Match: 24$"},
+ {"c": "no match dscp cs3", "tN": r"DSCP Match: 24$"},
+ {"c": "match dscp 5", "tm": r"DSCP Match: 5$"},
+ {"c": "no match dscp 5", "tN": r"DSCP Match: 5$"},
+ {"c": "match ecn 2", "tm": r"ECN Match: 2$"},
+ {"c": "no match ecn 2", "tN": r"ECN Match: 2$"},
+ {"c": "match mark 337", "tm": r"MARK Match: 337$"},
+ {"c": "no match mark 337", "tN": r"MARK Match: 337$"},
+ {"c": "set src-ip 44.100.1.1", "tm": r"Set SRC IP: 44.100.1.1$"},
+ {"c": "no set src-ip 44.100.1.1", "tN": r"Set SRC IP: 44.100.1.1$"},
+ {"c": "set dst-ip 44.105.1.1", "tm": r"Set DST IP: 44.105.1.1$"},
+ {"c": "no set dst-ip 44.105.1.1", "tN": r"Set DST IP: 44.105.1.1$"},
+ {"c": "set src-port 41", "tm": r"Set SRC PORT: 41$"},
+ {"c": "no set src-port 41", "tN": r"Set SRC PORT: 41$"},
+ {"c": "set dst-port 43", "tm": r"Set DST PORT: 43$"},
+ {"c": "no set dst-port 43", "tN": r"Set DST PORT: 43$"},
+ {"c": "set dscp 24", "tm": r"Set DSCP: 24$"},
+ {"c": "no set dscp 24", "tN": r"Set DSCP: 24$"},
+ {"c": "set dscp cs7", "tm": r"Set DSCP: 56$"},
+ {"c": "no set dscp cs7", "tN": r"Set DSCP: 56$"},
+ {"c": "set ecn 1", "tm": r"Set ECN: 1$"},
+ {"c": "no set ecn 1", "tN": r"Set ECN: 1$"},
+]
+
+
+# returns None if command output is correct, otherwise returns output
+def rtr_field_cmp(rtr, cmd, pat_mustmatch, pat_mustnotmatch):
+ outstr = rtr.vtysh_cmd(cmd)
+ if pat_mustmatch is not None:
+ logger.info("MUSTMATCH: {}".format(pat_mustmatch))
+ m = re.search(pat_mustmatch, outstr, flags=re.M)
+ if not m:
+ logger.info('Missing MUSTMATCH "{}"'.format(pat_mustmatch))
+ return "MISSING MUSTMATCH: " + outstr
+ if pat_mustnotmatch is not None:
+ logger.info("MUSTNOTMATCH: {}".format(pat_mustnotmatch))
+ m = re.search(pat_mustnotmatch, outstr, flags=re.M)
+ if m:
+ logger.info('Has MUSTNOTMATCH "{}"'.format(pat_mustnotmatch))
+ return "HAS MUSTNOTMATCH: " + outstr
+ return None
+
+
+#
+# This test sets fields in pbrd and looks for them in zebra via "sh pbr map"
+#
+def test_pbr_fields():
+ "Test setting and clearing rule fields"
+
+ 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 PBR rule fields")
+
+ # uncomment for manual interaction
+ # tgen.cli()
+
+ tag = "field"
+
+ router_list = tgen.routers().values()
+ for router in router_list:
+ for t in ftest:
+ # send field-setting command
+ # always have a match dst-ip to satisfy rule non-empty check
+ if "cDN" in t:
+ match_dstip = ""
+ else:
+ match_dstip = "match dst-ip 9.9.9.9/32\n"
+ vcmd = "c t\npbr-map ASAKUSA seq 100\n{}\n{}set nexthop-group A\nend\nend".format(
+ t["c"], match_dstip
+ )
+ router.vtysh_multicmd(vcmd)
+
+ # debug
+ router.vtysh_cmd("sh pbr map")
+
+ match = None
+ notmatch = None
+
+ if "tm" in t:
+ match = t["tm"]
+ logger.info("MUSTMATCH: {}".format(match))
+ if "tN" in t:
+ notmatch = t["tN"]
+ logger.info("NOTMATCH: {}".format(notmatch))
+
+ test_func = partial(rtr_field_cmp, router, "sh pbr rule", match, notmatch)
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = '"{}" mismatches on {}'.format(tag, router.name)
+ if result is not None:
+ gather_pbr_data_on_error(router)
+ assert result is None, assertmsg
+
+ #
+ # clean up
+ #
+ vcmd = "c t\nno pbr-map ASAKUSA seq 100\nend"
+ router.vtysh_multicmd(vcmd)
+
+ match = None
+ notmatch = r"Seq 100\w"
+
+ test_func = partial(rtr_field_cmp, router, "sh pbr rule", match, notmatch)
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = '"{}" mismatches on {}'.format(tag, router.name)
+ if result is not None:
+ gather_pbr_data_on_error(router)
+ assert result is None, assertmsg
+
+
+########################################################################
+# Field test - END
+########################################################################
+
+
+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..34ce481
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/ospf_neighbor.json
@@ -0,0 +1,59 @@
+{
+ "neighbors":{
+ "192.168.0.11":[
+ {
+ "nbrPriority":10,
+ "converged":"Full",
+ "ifaceAddress":"192.168.101.11",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "linkStateRetransmissionListCounter":0,
+ "linkStateRequestListCounter":0,
+ "databaseSummaryListCounter":0
+ }
+ ],
+ "192.168.0.12":[
+ {
+ "nbrPriority":0,
+ "converged":"Full",
+ "ifaceAddress":"192.168.101.12",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "linkStateRetransmissionListCounter":0,
+ "linkStateRequestListCounter":0,
+ "databaseSummaryListCounter":0
+ }
+ ],
+ "192.168.0.13":[
+ {
+ "nbrPriority":0,
+ "converged":"Full",
+ "ifaceAddress":"192.168.101.13",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "linkStateRetransmissionListCounter":0,
+ "linkStateRequestListCounter":0,
+ "databaseSummaryListCounter":0
+ }
+ ],
+ "192.168.0.14":[
+ {
+ "nbrPriority":0,
+ "converged":"Full",
+ "ifaceAddress":"192.168.101.14",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "linkStateRetransmissionListCounter":0,
+ "linkStateRequestListCounter":0,
+ "databaseSummaryListCounter":0
+ }
+ ],
+ "192.168.0.15":[
+ {
+ "nbrPriority":0,
+ "converged":"Full",
+ "ifaceAddress":"192.168.101.15",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "linkStateRetransmissionListCounter":0,
+ "linkStateRequestListCounter":0,
+ "databaseSummaryListCounter":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..6e5092d
--- /dev/null
+++ b/tests/topotests/pim_acl/test_pim_acl.py
@@ -0,0 +1,332 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_pim_acl.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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..d05ed1a
--- /dev/null
+++ b/tests/topotests/pim_basic/mcast-rx.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: ISC
+#
+# mcast-rx.py
+#
+# Copyright (c) 2018 Cumulus Networks, Inc.
+#
+"""
+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..ed3faf4
--- /dev/null
+++ b/tests/topotests/pim_basic/mcast-tx.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: ISC
+#
+# mcast-tx.py
+#
+# Copyright (c) 2018 Cumulus Networks, Inc.
+#
+
+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..24987e5
--- /dev/null
+++ b/tests/topotests/pim_basic/test_pim.py
@@ -0,0 +1,235 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_pim.py
+#
+# Copyright (c) 2018 Cumulus Networks, Inc.
+# Donald Sharp
+#
+
+"""
+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=30, wait=1)
+ 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 40 --interval 2 229.1.1.1 r2-eth0 > /tmp/bar".format(
+ CWD
+ )
+ )
+ # And from r3 -> r1
+ r3.run(
+ "{}/mcast-tx.py --ttl 5 --count 40 --interval 2 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=40, 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=40, wait=1)
+ 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=40, wait=1)
+ 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..cb6d0d7
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/test_pim_basic_topo2.py
@@ -0,0 +1,225 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_pim_basic_topo2.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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=5, 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=5, 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..198098d
--- /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":[
+ {
+ "nbrPriority":10,
+ "converged":"Full",
+ "ifaceAddress":"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..6fce225
--- /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":[
+ {
+ "nbrPriority":10,
+ "converged":"Full",
+ "ifaceAddress":"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..ddc4303
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/test_pim_vrf.py
@@ -0,0 +1,389 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_pim_vrf.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+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()
+
+ # 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")
+
+ 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()
+
+ # 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..6c2d42e
--- /dev/null
+++ b/tests/topotests/pytest.ini
@@ -0,0 +1,87 @@
+# Skip pytests example directory
+[pytest]
+
+# asyncio_mode = auto
+
+# We always turn this on inside conftest.py, default shown
+# addopts = --junitxml=<rundir>/topotests.xml
+
+# This affects what gets dumped to the screen on test failure
+log_level = ERROR
+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_munet example_test example_topojson_test lib munet 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
+ pim6d: Tests that run against PIM6D
+ 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_allow_ecmp/__init__.py b/tests/topotests/rip_allow_ecmp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/rip_allow_ecmp/__init__.py
diff --git a/tests/topotests/rip_allow_ecmp/r1/frr.conf b/tests/topotests/rip_allow_ecmp/r1/frr.conf
new file mode 100644
index 0000000..d8eb9a3
--- /dev/null
+++ b/tests/topotests/rip_allow_ecmp/r1/frr.conf
@@ -0,0 +1,9 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
+router rip
+ allow-ecmp
+ network 192.168.1.0/24
+ timers basic 5 15 10
+exit
diff --git a/tests/topotests/rip_allow_ecmp/r2/frr.conf b/tests/topotests/rip_allow_ecmp/r2/frr.conf
new file mode 100644
index 0000000..d7ea6f3
--- /dev/null
+++ b/tests/topotests/rip_allow_ecmp/r2/frr.conf
@@ -0,0 +1,13 @@
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+router rip
+ network 192.168.1.0/24
+ network 10.10.10.1/32
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/rip_allow_ecmp/r3/frr.conf b/tests/topotests/rip_allow_ecmp/r3/frr.conf
new file mode 100644
index 0000000..2362c47
--- /dev/null
+++ b/tests/topotests/rip_allow_ecmp/r3/frr.conf
@@ -0,0 +1,13 @@
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r3-eth0
+ ip address 192.168.1.3/24
+!
+router rip
+ network 192.168.1.0/24
+ network 10.10.10.1/32
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/rip_allow_ecmp/r4/frr.conf b/tests/topotests/rip_allow_ecmp/r4/frr.conf
new file mode 100644
index 0000000..995c2be
--- /dev/null
+++ b/tests/topotests/rip_allow_ecmp/r4/frr.conf
@@ -0,0 +1,13 @@
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r4-eth0
+ ip address 192.168.1.4/24
+!
+router rip
+ network 192.168.1.0/24
+ network 10.10.10.1/32
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/rip_allow_ecmp/r5/frr.conf b/tests/topotests/rip_allow_ecmp/r5/frr.conf
new file mode 100644
index 0000000..57a06ec
--- /dev/null
+++ b/tests/topotests/rip_allow_ecmp/r5/frr.conf
@@ -0,0 +1,13 @@
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r5-eth0
+ ip address 192.168.1.5/24
+!
+router rip
+ network 192.168.1.0/24
+ network 10.10.10.1/32
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py b/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py
new file mode 100644
index 0000000..7d958fd
--- /dev/null
+++ b/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if RIP `allow-ecmp` command works 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
+from lib.common_config import step
+
+pytestmark = [pytest.mark.ripd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2", "r3", "r4", "r5")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for _, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_rip_allow_ecmp():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ def _show_rip_routes():
+ xpath = (
+ "/frr-ripd:ripd/instance[vrf='default']"
+ "/state/routes/route[prefix='10.10.10.1/32']"
+ )
+ try:
+ output = json.loads(
+ r1.vtysh_cmd(f"show yang operational-data {xpath} ripd")
+ )
+ except Exception:
+ return False
+
+ try:
+ output = output["frr-ripd:ripd"]["instance"][0]["state"]["routes"]
+ except KeyError:
+ return False
+
+ expected = {
+ "route": [
+ {
+ "prefix": "10.10.10.1/32",
+ "nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "ip4",
+ "protocol": "rip",
+ "rip-type": "normal",
+ "gateway": "192.168.1.2",
+ "from": "192.168.1.2",
+ "tag": 0,
+ },
+ {
+ "nh-type": "ip4",
+ "protocol": "rip",
+ "rip-type": "normal",
+ "gateway": "192.168.1.3",
+ "from": "192.168.1.3",
+ "tag": 0,
+ },
+ ]
+ },
+ "metric": 2,
+ },
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_show_rip_routes)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Can't see 10.10.10.1/32 as multipath in `show ip rip`"
+
+ def _show_routes(nh_num):
+ output = json.loads(r1.vtysh_cmd("show ip route json"))
+ expected = {
+ "10.10.10.1/32": [
+ {
+ "internalNextHopNum": nh_num,
+ "internalNextHopActiveNum": nh_num,
+ "nexthops": [
+ {
+ "ip": "192.168.1.2",
+ "active": True,
+ },
+ {
+ "ip": "192.168.1.3",
+ "active": True,
+ },
+ ],
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_show_routes, 4)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Can't see 10.10.10.1/32 as multipath (4) in `show ip route`"
+
+ step(
+ "Configure allow-ecmp 2, ECMP group routes SHOULD have next-hops with the lowest IPs"
+ )
+ r1.vtysh_cmd(
+ """
+ configure terminal
+ router rip
+ allow-ecmp 2
+ """
+ )
+
+ test_func = functools.partial(_show_rip_routes)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert (
+ result is None
+ ), "Can't see 10.10.10.1/32 as ECMP with the lowest next-hop IPs"
+
+ test_func = functools.partial(_show_routes, 2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Can't see 10.10.10.1/32 as multipath (2) in `show ip route`"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/rip_bfd_topo1/__init__.py b/tests/topotests/rip_bfd_topo1/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/__init__.py
diff --git a/tests/topotests/rip_bfd_topo1/r1/bfdd.conf b/tests/topotests/rip_bfd_topo1/r1/bfdd.conf
new file mode 100644
index 0000000..e848bda
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r1/bfdd.conf
@@ -0,0 +1,6 @@
+bfd
+ profile slow
+ receive-interval 1000
+ transmit-interval 1000
+ exit
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r1/ripd.conf b/tests/topotests/rip_bfd_topo1/r1/ripd.conf
new file mode 100644
index 0000000..6cef846
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r1/ripd.conf
@@ -0,0 +1,17 @@
+interface r1-eth0
+ ip rip bfd
+ ip rip bfd profile slow
+exit
+!
+interface r1-eth1
+ ip rip bfd
+ ip rip bfd profile slow
+exit
+!
+router rip
+ allow-ecmp
+ network 192.168.0.1/24
+ network 192.168.1.1/24
+ redistribute connected
+ timers basic 10 40 30
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r1/zebra.conf b/tests/topotests/rip_bfd_topo1/r1/zebra.conf
new file mode 100644
index 0000000..e3800a9
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r1/zebra.conf
@@ -0,0 +1,11 @@
+interface r1-eth0
+ ip address 192.168.0.1/24
+exit
+!
+interface r1-eth1
+ ip address 192.168.1.1/24
+exit
+!
+interface lo
+ ip address 10.254.254.1/32
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r2/bfdd.conf b/tests/topotests/rip_bfd_topo1/r2/bfdd.conf
new file mode 100644
index 0000000..e848bda
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r2/bfdd.conf
@@ -0,0 +1,6 @@
+bfd
+ profile slow
+ receive-interval 1000
+ transmit-interval 1000
+ exit
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r2/ripd.conf b/tests/topotests/rip_bfd_topo1/r2/ripd.conf
new file mode 100644
index 0000000..35e4688
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r2/ripd.conf
@@ -0,0 +1,11 @@
+interface r2-eth0
+ ip rip bfd
+exit
+!
+router rip
+ bfd default-profile slow
+ network 192.168.0.2/24
+ redistribute connected
+ redistribute static
+ timers basic 10 40 30
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r2/staticd.conf b/tests/topotests/rip_bfd_topo1/r2/staticd.conf
new file mode 100644
index 0000000..6fe9374
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r2/staticd.conf
@@ -0,0 +1 @@
+ip route 10.254.254.100/32 lo
diff --git a/tests/topotests/rip_bfd_topo1/r2/zebra.conf b/tests/topotests/rip_bfd_topo1/r2/zebra.conf
new file mode 100644
index 0000000..cad922f
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r2/zebra.conf
@@ -0,0 +1,8 @@
+interface r2-eth0
+ ip address 192.168.0.2/24
+exit
+!
+interface lo
+ ip address 10.254.254.2/32
+exit
+
diff --git a/tests/topotests/rip_bfd_topo1/r3/bfdd.conf b/tests/topotests/rip_bfd_topo1/r3/bfdd.conf
new file mode 100644
index 0000000..e848bda
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r3/bfdd.conf
@@ -0,0 +1,6 @@
+bfd
+ profile slow
+ receive-interval 1000
+ transmit-interval 1000
+ exit
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r3/ripd.conf b/tests/topotests/rip_bfd_topo1/r3/ripd.conf
new file mode 100644
index 0000000..0df0bac
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r3/ripd.conf
@@ -0,0 +1,11 @@
+interface r3-eth0
+ ip rip bfd
+ ip rip bfd profile slow
+exit
+!
+router rip
+ network 192.168.1.2/24
+ redistribute connected
+ redistribute static
+ timers basic 10 40 30
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r3/staticd.conf b/tests/topotests/rip_bfd_topo1/r3/staticd.conf
new file mode 100644
index 0000000..6fe9374
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r3/staticd.conf
@@ -0,0 +1 @@
+ip route 10.254.254.100/32 lo
diff --git a/tests/topotests/rip_bfd_topo1/r3/zebra.conf b/tests/topotests/rip_bfd_topo1/r3/zebra.conf
new file mode 100644
index 0000000..12ffeca
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r3/zebra.conf
@@ -0,0 +1,7 @@
+interface r3-eth0
+ ip address 192.168.1.2/24
+exit
+!
+interface lo
+ ip address 10.254.254.3/32
+exit
diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot
new file mode 100644
index 0000000..1480a8f
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.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="rip_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,
+ ];
+
+ # Switches
+ s1 [
+ shape=oval,
+ label="s1\n192.168.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ shape=oval,
+ label="s1\n192.168.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- s1 [label="r1-eth0\n.1"];
+ r2 -- s1 [label="r2-eth0\n.2"];
+
+ r1 -- s2 [label="r1-eth1\n.1"];
+ r3 -- s2 [label="r1-eth0\n.2"];
+}
diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png
new file mode 100644
index 0000000..e5e362e
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png
Binary files differ
diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py
new file mode 100644
index 0000000..71c9093
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py
@@ -0,0 +1,252 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_rip_bfd_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2023 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+test_rip_bfd_topo1.py: Test RIP BFD integration.
+"""
+
+import sys
+import re
+import pytest
+
+from functools import partial
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter
+from lib.topolog import logger
+
+pytestmark = [
+ pytest.mark.bfdd,
+ pytest.mark.ripd,
+]
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ topodef = {
+ "s1": ("r1", "r2"),
+ "s2": ("r1", "r3")
+ }
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for router_name, router in router_list.items():
+ router.load_config(TopoRouter.RD_BFD, "bfdd.conf")
+ router.load_config(TopoRouter.RD_RIP, "ripd.conf")
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ if router_name in ["r2", "r3"]:
+ router.load_config(TopoRouter.RD_STATIC, "staticd.conf")
+
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ "Test if routers is still running otherwise skip tests"
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+def show_rip_json(router):
+ "Get router 'show ip rip' JSON output"
+ output = router.vtysh_cmd("show ip rip")
+ routes = output.splitlines()[6:]
+ json = {}
+
+ for route in routes:
+ match = re.match(
+ r"(.)\((.)\)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)", route)
+ if match is None:
+ continue
+
+ route_entry = {
+ "code": match[1],
+ "subCode": match[2],
+ "nextHop": match[4],
+ "metric": int(match[5]),
+ "from": match[6],
+ }
+
+ if json.get(match[3]) is None:
+ json[match[3]] = []
+
+ json[match[3]].append(route_entry)
+
+ return json
+
+
+def expect_routes(router, routes, time_amount):
+ "Expect 'routes' in 'router'."
+
+ def test_function():
+ "Internal test function."
+ return topotest.json_cmp(show_rip_json(router), routes)
+
+ _, result = topotest.run_and_expect(test_function,
+ None,
+ count=time_amount,
+ wait=1)
+ assert result is None, "Unexpected routing table in {}".format(
+ router.name)
+
+
+def expect_bfd_peers(router, peers):
+ "Expect 'peers' in 'router' BFD status."
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show bfd peers json",
+ peers,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assert result is None, "{} BFD peer status mismatch".format(router)
+
+
+def test_rip_convergence(tgen):
+ "Test that RIP learns the neighbor routes."
+
+ expect_routes(
+ tgen.gears["r1"], {
+ "10.254.254.2/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.2"
+ }],
+ "10.254.254.3/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.1.2"
+ }],
+ "10.254.254.100/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.2",
+ }, {
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.1.2",
+ }]
+ }, 40)
+
+ expect_bfd_peers(tgen.gears["r1"], [{
+ "peer": "192.168.0.2",
+ "status": "up",
+ "receive-interval": 1000,
+ "transmit-interval": 1000,
+ }, {
+ "peer": "192.168.1.2",
+ "status": "up",
+ "receive-interval": 1000,
+ "transmit-interval": 1000,
+ }])
+
+ expect_routes(
+ tgen.gears["r2"], {
+ "10.254.254.1/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.1"
+ }],
+ "10.254.254.3/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.1"
+ }],
+ "10.254.254.100/32": [{
+ "code": "S",
+ "subCode": "r",
+ "from": "self"
+ }]
+ }, 40)
+
+ expect_bfd_peers(tgen.gears["r2"], [{
+ "peer": "192.168.0.1",
+ "status": "up",
+ "receive-interval": 1000,
+ "transmit-interval": 1000,
+ }])
+
+ expect_routes(
+ tgen.gears["r3"], {
+ "10.254.254.1/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.1.1"
+ }],
+ "10.254.254.2/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.1.1"
+ }],
+ "10.254.254.100/32": [{
+ "code": "S",
+ "subCode": "r",
+ "from": "self"
+ }]
+ }, 40)
+
+ expect_bfd_peers(tgen.gears["r3"], [{
+ "peer": "192.168.1.1",
+ "status": "up",
+ "receive-interval": 1000,
+ "transmit-interval": 1000,
+ }])
+
+
+def test_rip_bfd_convergence(tgen):
+ "Test that RIP drop the gone neighbor routes."
+
+ tgen.gears["r3"].link_enable("r3-eth0", False)
+
+ expect_routes(
+ tgen.gears["r1"], {
+ "10.254.254.2/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.2"
+ }],
+ "10.254.254.3/32": None,
+ "10.254.254.100/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.2",
+ }]
+ }, 6)
+
+ expect_routes(
+ tgen.gears["r3"], {
+ "10.254.254.1/32": None,
+ "10.254.254.2/32": None,
+ "10.254.254.100/32": [{
+ "code": "S",
+ "subCode": "r",
+ "from": "self"
+ }]
+ }, 6)
+
+
+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/rip_passive_interface/__init__.py b/tests/topotests/rip_passive_interface/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/rip_passive_interface/__init__.py
diff --git a/tests/topotests/rip_passive_interface/r1/frr.conf b/tests/topotests/rip_passive_interface/r1/frr.conf
new file mode 100644
index 0000000..d8eb9a3
--- /dev/null
+++ b/tests/topotests/rip_passive_interface/r1/frr.conf
@@ -0,0 +1,9 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
+router rip
+ allow-ecmp
+ network 192.168.1.0/24
+ timers basic 5 15 10
+exit
diff --git a/tests/topotests/rip_passive_interface/r2/frr.conf b/tests/topotests/rip_passive_interface/r2/frr.conf
new file mode 100644
index 0000000..d7ea6f3
--- /dev/null
+++ b/tests/topotests/rip_passive_interface/r2/frr.conf
@@ -0,0 +1,13 @@
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+router rip
+ network 192.168.1.0/24
+ network 10.10.10.1/32
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/rip_passive_interface/r3/frr.conf b/tests/topotests/rip_passive_interface/r3/frr.conf
new file mode 100644
index 0000000..2362c47
--- /dev/null
+++ b/tests/topotests/rip_passive_interface/r3/frr.conf
@@ -0,0 +1,13 @@
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r3-eth0
+ ip address 192.168.1.3/24
+!
+router rip
+ network 192.168.1.0/24
+ network 10.10.10.1/32
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/rip_passive_interface/test_rip_passive_interface.py b/tests/topotests/rip_passive_interface/test_rip_passive_interface.py
new file mode 100644
index 0000000..c2b28c4
--- /dev/null
+++ b/tests/topotests/rip_passive_interface/test_rip_passive_interface.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if RIP `passive-interface default` and `no passive-interface IFNAME`
+combination works as expected.
+"""
+
+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.ripd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2", "r3")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for _, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_rip_passive_interface():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _show_routes(nh_num):
+ output = json.loads(r1.vtysh_cmd("show ip route 10.10.10.1/32 json"))
+ expected = {
+ "10.10.10.1/32": [
+ {
+ "internalNextHopNum": nh_num,
+ "internalNextHopActiveNum": nh_num,
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_show_routes, 2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 2"
+
+ step("Configure `passive-interface default` on r2")
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router rip
+ passive-interface default
+ """
+ )
+
+ test_func = functools.partial(_show_routes, 1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 1"
+
+ step("Configure `no passive-interface r2-eth0` on r2 towards r1")
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router rip
+ no passive-interface r2-eth0
+ """
+ )
+
+ test_func = functools.partial(_show_routes, 2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
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..19fff1a
--- /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..468b7ae
--- /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..f423e83
--- /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..9b0eaf9
--- /dev/null
+++ b/tests/topotests/rip_topo1/test_rip_topo1.py
@@ -0,0 +1,336 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_rip_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+test_rip_topo1.py: Testing RIPv2
+
+"""
+
+import os
+import re
+import sys
+import pytest
+from time import sleep
+import functools
+
+
+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
+
+ def _verify_ip_route(expected):
+ # 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)
+
+ return topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual Zebra IPv4 routing table",
+ title2="expected Zebra IPv4 routing table",
+ )
+
+ # 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** Verifing Zebra IPv4 Routing Table")
+ print("******************************************\n")
+ for i in range(1, 4):
+ refTableFile = "%s/r%s/show_ip_route.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ test_func = functools.partial(_verify_ip_route, expected)
+ success, _ = topotest.run_and_expect(test_func, "", count=30, wait=1)
+ assert success, "Failed verifying IPv4 routes for r{}".format(i)
+
+ # 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_allow_ecmp/__init__.py b/tests/topotests/ripng_allow_ecmp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ripng_allow_ecmp/__init__.py
diff --git a/tests/topotests/ripng_allow_ecmp/r1/frr.conf b/tests/topotests/ripng_allow_ecmp/r1/frr.conf
new file mode 100644
index 0000000..effb5df
--- /dev/null
+++ b/tests/topotests/ripng_allow_ecmp/r1/frr.conf
@@ -0,0 +1,9 @@
+!
+int r1-eth0
+ ipv6 address 2001:db8:1::1/64
+!
+router ripng
+ allow-ecmp
+ network 2001:db8:1::/64
+ timers basic 5 15 10
+exit
diff --git a/tests/topotests/ripng_allow_ecmp/r2/frr.conf b/tests/topotests/ripng_allow_ecmp/r2/frr.conf
new file mode 100644
index 0000000..71da101
--- /dev/null
+++ b/tests/topotests/ripng_allow_ecmp/r2/frr.conf
@@ -0,0 +1,13 @@
+!
+int lo
+ ipv6 address 2001:db8:2::1/64
+!
+int r2-eth0
+ ipv6 address 2001:db8:1::2/64
+!
+router ripng
+ redistribute connected
+ network 2001:db8:1::/64
+ network 2001:db8:2::/64
+ timers basic 5 15 10
+exit
diff --git a/tests/topotests/ripng_allow_ecmp/r3/frr.conf b/tests/topotests/ripng_allow_ecmp/r3/frr.conf
new file mode 100644
index 0000000..fe9594d
--- /dev/null
+++ b/tests/topotests/ripng_allow_ecmp/r3/frr.conf
@@ -0,0 +1,14 @@
+!
+int lo
+ ipv6 address 2001:db8:2::1/64
+!
+int r3-eth0
+ ipv6 address 2001:db8:1::3/64
+!
+router ripng
+ redistribute connected
+ network 2001:db8:1::/64
+ network 2001:db8:2::/64
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/ripng_allow_ecmp/r4/frr.conf b/tests/topotests/ripng_allow_ecmp/r4/frr.conf
new file mode 100644
index 0000000..0d3ea0b
--- /dev/null
+++ b/tests/topotests/ripng_allow_ecmp/r4/frr.conf
@@ -0,0 +1,14 @@
+!
+int lo
+ ipv6 address 2001:db8:2::1/64
+!
+int r4-eth0
+ ipv6 address 2001:db8:1::4/64
+!
+router ripng
+ redistribute connected
+ network 2001:db8:1::/64
+ network 2001:db8:2::/64
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/ripng_allow_ecmp/r5/frr.conf b/tests/topotests/ripng_allow_ecmp/r5/frr.conf
new file mode 100644
index 0000000..6d6ca56
--- /dev/null
+++ b/tests/topotests/ripng_allow_ecmp/r5/frr.conf
@@ -0,0 +1,14 @@
+!
+int lo
+ ipv6 address 2001:db8:2::1/64
+!
+int r5-eth0
+ ipv6 address 2001:db8:1::5/64
+!
+router ripng
+ redistribute connected
+ network 2001:db8:1::/64
+ network 2001:db8:2::/64
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/ripng_allow_ecmp/test_ripng_allow_ecmp.py b/tests/topotests/ripng_allow_ecmp/test_ripng_allow_ecmp.py
new file mode 100644
index 0000000..08bb999
--- /dev/null
+++ b/tests/topotests/ripng_allow_ecmp/test_ripng_allow_ecmp.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if RIPng `allow-ecmp` command works 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
+from lib.common_config import step
+
+pytestmark = [pytest.mark.ripngd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2", "r3", "r4", "r5")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for _, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_ripng_allow_ecmp():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ def _show_routes(nh_num):
+ output = json.loads(r1.vtysh_cmd("show ipv6 route json"))
+ expected = {
+ "2001:db8:2::/64": [
+ {
+ "internalNextHopNum": nh_num,
+ "internalNextHopActiveNum": nh_num,
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_show_routes, 4)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert (
+ result is None
+ ), "Can't see 2001:db8:2::/64 as multipath (4) in `show ipv6 route`"
+
+ step(
+ "Configure allow-ecmp 2, ECMP group routes SHOULD have next-hops with the lowest IPs"
+ )
+ r1.vtysh_cmd(
+ """
+ configure terminal
+ router ripng
+ allow-ecmp 2
+ """
+ )
+
+ test_func = functools.partial(_show_routes, 2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert (
+ result is None
+ ), "Can't see 2001:db8:2::/64 as multipath (2) in `show ipv6 route`"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ripng_route_map/__init__.py b/tests/topotests/ripng_route_map/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ripng_route_map/__init__.py
diff --git a/tests/topotests/ripng_route_map/r1/frr.conf b/tests/topotests/ripng_route_map/r1/frr.conf
new file mode 100644
index 0000000..6d0fb46
--- /dev/null
+++ b/tests/topotests/ripng_route_map/r1/frr.conf
@@ -0,0 +1,21 @@
+!
+int r1-eth0
+ ipv6 address 2001:db8::1/64
+!
+router ripng
+ network 2001:db8::/64
+ route-map rmap in r1-eth0
+ timers basic 5 15 10
+exit
+!
+ipv6 access-list r2 seq 5 permit 2001:db8:2::/64
+!
+ipv6 prefix-list r3 seq 5 permit 2001:db8:3::/64
+!
+route-map rmap permit 10
+ match ipv6 address r2
+ set metric 12
+route-map rmap permit 20
+ match ipv6 address prefix-list r3
+ set metric 13
+exit
diff --git a/tests/topotests/ripng_route_map/r2/frr.conf b/tests/topotests/ripng_route_map/r2/frr.conf
new file mode 100644
index 0000000..fb9d670
--- /dev/null
+++ b/tests/topotests/ripng_route_map/r2/frr.conf
@@ -0,0 +1,14 @@
+!
+int lo
+ ipv6 address 2001:db8:2::1/64
+!
+int r2-eth0
+ ipv6 address 2001:db8::2/64
+!
+router ripng
+ redistribute connected
+ network 2001:db8::/64
+ network 2001:db8:2::1/64
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/ripng_route_map/r3/frr.conf b/tests/topotests/ripng_route_map/r3/frr.conf
new file mode 100644
index 0000000..a6d0778
--- /dev/null
+++ b/tests/topotests/ripng_route_map/r3/frr.conf
@@ -0,0 +1,14 @@
+!
+int lo
+ ipv6 address 2001:db8:3::1/64
+!
+int r3-eth0
+ ipv6 address 2001:db8::3/64
+!
+router ripng
+ redistribute connected
+ network 2001:db8::/64
+ network 2001:db8:3::1/64
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/ripng_route_map/test_ripng_route_map.py b/tests/topotests/ripng_route_map/test_ripng_route_map.py
new file mode 100644
index 0000000..e1cc88e
--- /dev/null
+++ b/tests/topotests/ripng_route_map/test_ripng_route_map.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if route-map for ripng basic functionality 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
+from lib.common_config import step
+
+pytestmark = [pytest.mark.ripngd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2", "r3")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for _, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_ripng_route_map():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _show_routes(nh_num):
+ output = json.loads(r1.vtysh_cmd("show ipv6 route ripng json"))
+ expected = {
+ "2001:db8:2::/64": [
+ {
+ "metric": 13,
+ }
+ ],
+ "2001:db8:3::/64": [
+ {
+ "metric": 14,
+ }
+ ],
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_show_routes, 2)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Got routes, but metric is not set as expected"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
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..6bebf60
--- /dev/null
+++ b/tests/topotests/ripng_topo1/test_ripng_topo1.py
@@ -0,0 +1,380 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ripng_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+test_ripng_topo1.py: Test of RIPng Topology
+
+"""
+
+import os
+import re
+import sys
+import pytest
+from time import sleep
+import functools
+
+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
+
+ def _verify_ip_route(expected):
+ # 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)
+
+ return topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual Zebra IPv6 routing table",
+ title2="expected Zebra IPv6 routing table",
+ )
+
+ # 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)
+
+ test_func = functools.partial(_verify_ip_route, expected)
+ success, _ = topotest.run_and_expect(test_func, "", count=30, wait=1)
+ assert success, "Failed verifying IPv6 routes for r{}".format(i)
+
+ # 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..6227e81
--- /dev/null
+++ b/tests/topotests/route_scale/scale_test_common.py
@@ -0,0 +1,202 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# scale_test_common.py
+#
+# Copyright (c) 2020 by
+# Cumulus Networks, Inc.
+# Donald Sharp
+#
+
+"""
+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, 10, 50, 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..0f25b28
--- /dev/null
+++ b/tests/topotests/route_scale/test_route_scale1.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_route_scale1.py
+#
+# Copyright (c) 2021 by
+# Nvidia, Inc.
+# Donald Sharp
+#
+
+"""
+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..3b55fcd
--- /dev/null
+++ b/tests/topotests/route_scale/test_route_scale2.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_route_scale2.py
+#
+# Copyright (c) 2022 by
+# Nvidia, Inc.
+# Donald Sharp
+#
+
+"""
+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..435abde
--- /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 10
+ 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 10
+ 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 10
+ 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..cdcd9a2
--- /dev/null
+++ b/tests/topotests/simple_snmp_test/r1/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp: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..ee02c7b
--- /dev/null
+++ b/tests/topotests/simple_snmp_test/test_simple_snmp.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_simple_snmp.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+
+"""
+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..3a27d9c
--- /dev/null
+++ b/tests/topotests/srv6_locator/test_srv6_locator.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_srv6_manager.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# LINE Corporation, Hiroki Shirokura <slank.dev@gmail.com>
+#
+
+"""
+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=10, 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=10, 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/srv6_locator_custom_bits_length/__init__.py b/tests/topotests/srv6_locator_custom_bits_length/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/__init__.py
diff --git a/tests/topotests/srv6_locator_custom_bits_length/expected_chunks1.json b/tests/topotests/srv6_locator_custom_bits_length/expected_chunks1.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/expected_chunks1.json
@@ -0,0 +1 @@
+[]
diff --git a/tests/topotests/srv6_locator_custom_bits_length/expected_chunks2.json b/tests/topotests/srv6_locator_custom_bits_length/expected_chunks2.json
new file mode 100644
index 0000000..9f34c58
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/expected_chunks2.json
@@ -0,0 +1,8 @@
+[
+ {
+ "name": "loc1",
+ "chunks": [
+ "2001:db8:1::/64"
+ ]
+ }
+]
diff --git a/tests/topotests/srv6_locator_custom_bits_length/expected_chunks3.json b/tests/topotests/srv6_locator_custom_bits_length/expected_chunks3.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/expected_chunks3.json
@@ -0,0 +1 @@
+[]
diff --git a/tests/topotests/srv6_locator_custom_bits_length/expected_chunks4.json b/tests/topotests/srv6_locator_custom_bits_length/expected_chunks4.json
new file mode 100644
index 0000000..0d4f101
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/expected_chunks4.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/tests/topotests/srv6_locator_custom_bits_length/expected_chunks5.json b/tests/topotests/srv6_locator_custom_bits_length/expected_chunks5.json
new file mode 100644
index 0000000..0d4f101
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/expected_chunks5.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/tests/topotests/srv6_locator_custom_bits_length/expected_chunks6.json b/tests/topotests/srv6_locator_custom_bits_length/expected_chunks6.json
new file mode 100644
index 0000000..0d4f101
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/expected_chunks6.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/tests/topotests/srv6_locator_custom_bits_length/expected_locators1.json b/tests/topotests/srv6_locator_custom_bits_length/expected_locators1.json
new file mode 100644
index 0000000..e89a56a
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/expected_locators1.json
@@ -0,0 +1,34 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "2001:db8:1::/64",
+ "blockBitsLength": 40,
+ "nodeBitsLength": 24,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:1::/64",
+ "proto": "system"
+ }
+ ]
+ },
+ {
+ "name": "loc2",
+ "prefix": "2001:db8:2::/48",
+ "blockBitsLength": 24,
+ "nodeBitsLength": 24,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:2::/48",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator_custom_bits_length/expected_locators2.json b/tests/topotests/srv6_locator_custom_bits_length/expected_locators2.json
new file mode 100644
index 0000000..f2ca09a
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/expected_locators2.json
@@ -0,0 +1,34 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "2001:db8:1::/64",
+ "blockBitsLength": 40,
+ "nodeBitsLength": 24,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:1::/64",
+ "proto": "sharp"
+ }
+ ]
+ },
+ {
+ "name": "loc2",
+ "prefix": "2001:db8:2::/48",
+ "blockBitsLength": 24,
+ "nodeBitsLength": 24,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:2::/48",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator_custom_bits_length/expected_locators3.json b/tests/topotests/srv6_locator_custom_bits_length/expected_locators3.json
new file mode 100644
index 0000000..e89a56a
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/expected_locators3.json
@@ -0,0 +1,34 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "2001:db8:1::/64",
+ "blockBitsLength": 40,
+ "nodeBitsLength": 24,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:1::/64",
+ "proto": "system"
+ }
+ ]
+ },
+ {
+ "name": "loc2",
+ "prefix": "2001:db8:2::/48",
+ "blockBitsLength": 24,
+ "nodeBitsLength": 24,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:2::/48",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator_custom_bits_length/expected_locators4.json b/tests/topotests/srv6_locator_custom_bits_length/expected_locators4.json
new file mode 100644
index 0000000..4c75d7f
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/expected_locators4.json
@@ -0,0 +1,49 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "2001:db8:1::/64",
+ "blockBitsLength": 40,
+ "nodeBitsLength": 24,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:1::/64",
+ "proto": "system"
+ }
+ ]
+ },
+ {
+ "name": "loc2",
+ "prefix": "2001:db8:2::/48",
+ "blockBitsLength": 24,
+ "nodeBitsLength": 24,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:2::/48",
+ "proto": "system"
+ }
+ ]
+ },
+ {
+ "name": "loc3",
+ "prefix": "2001:db8:3::/48",
+ "blockBitsLength": 32,
+ "nodeBitsLength": 16,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:3::/48",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator_custom_bits_length/expected_locators5.json b/tests/topotests/srv6_locator_custom_bits_length/expected_locators5.json
new file mode 100644
index 0000000..a01d313
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/expected_locators5.json
@@ -0,0 +1,34 @@
+{
+ "locators":[
+ {
+ "name": "loc2",
+ "prefix": "2001:db8:2::/48",
+ "blockBitsLength": 24,
+ "nodeBitsLength": 24,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:2::/48",
+ "proto": "system"
+ }
+ ]
+ },
+ {
+ "name": "loc3",
+ "prefix": "2001:db8:3::/48",
+ "statusUp": true,
+ "blockBitsLength": 32,
+ "nodeBitsLength": 16,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "chunks":[
+ {
+ "prefix": "2001:db8:3::/48",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator_custom_bits_length/expected_locators6.json b/tests/topotests/srv6_locator_custom_bits_length/expected_locators6.json
new file mode 100644
index 0000000..6e1b993
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/expected_locators6.json
@@ -0,0 +1,4 @@
+{
+ "locators":[
+ ]
+}
diff --git a/tests/topotests/srv6_locator_custom_bits_length/r1/setup.sh b/tests/topotests/srv6_locator_custom_bits_length/r1/setup.sh
new file mode 100644
index 0000000..36ed713
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/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_custom_bits_length/r1/sharpd.conf b/tests/topotests/srv6_locator_custom_bits_length/r1/sharpd.conf
new file mode 100644
index 0000000..d460859
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/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_custom_bits_length/r1/zebra.conf b/tests/topotests/srv6_locator_custom_bits_length/r1/zebra.conf
new file mode 100644
index 0000000..30a520b
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/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::/64
+ !
+ locator loc2
+ prefix 2001:db8:2::/48
+ !
+ !
+ !
+!
diff --git a/tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator.py b/tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator.py
new file mode 100755
index 0000000..92980d3
--- /dev/null
+++ b/tests/topotests/srv6_locator_custom_bits_length/test_srv6_locator.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2022, University of Rome Tor Vergata
+# Authored by Carmine Scarpitta <carmine.scarpitta@uniroma2.it>
+#
+
+"""
+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=10, 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=10, 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::/48 block-len 32 node-len 16 func-bits 16
+ """
+ )
+ 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/srv6_locator_usid/__init__.py b/tests/topotests/srv6_locator_usid/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/__init__.py
diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_1.json b/tests/topotests/srv6_locator_usid/expected_chunks_1.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_chunks_1.json
@@ -0,0 +1 @@
+[]
diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_2.json b/tests/topotests/srv6_locator_usid/expected_chunks_2.json
new file mode 100644
index 0000000..304d738
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_chunks_2.json
@@ -0,0 +1,8 @@
+[
+ {
+ "name": "loc1",
+ "chunks": [
+ "fc00:0:1::/48"
+ ]
+ }
+]
diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_3.json b/tests/topotests/srv6_locator_usid/expected_chunks_3.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_chunks_3.json
@@ -0,0 +1 @@
+[]
diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_4.json b/tests/topotests/srv6_locator_usid/expected_chunks_4.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_chunks_4.json
@@ -0,0 +1 @@
+[]
diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_5.json b/tests/topotests/srv6_locator_usid/expected_chunks_5.json
new file mode 100644
index 0000000..0d4f101
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_chunks_5.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_6.json b/tests/topotests/srv6_locator_usid/expected_chunks_6.json
new file mode 100644
index 0000000..0d4f101
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_chunks_6.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_7.json b/tests/topotests/srv6_locator_usid/expected_chunks_7.json
new file mode 100644
index 0000000..0d4f101
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_chunks_7.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_8.json b/tests/topotests/srv6_locator_usid/expected_chunks_8.json
new file mode 100644
index 0000000..0d4f101
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_chunks_8.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/tests/topotests/srv6_locator_usid/expected_locators_1.json b/tests/topotests/srv6_locator_usid/expected_locators_1.json
new file mode 100644
index 0000000..c0eeacc
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_locators_1.json
@@ -0,0 +1,20 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "fc00:0:1::/48",
+ "blockBitsLength": 32,
+ "nodeBitsLength": 16,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "behavior": "usid",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "fc00:0:1::/48",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator_usid/expected_locators_2.json b/tests/topotests/srv6_locator_usid/expected_locators_2.json
new file mode 100644
index 0000000..38a6739
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_locators_2.json
@@ -0,0 +1,20 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "fc00:0:1::/48",
+ "blockBitsLength": 32,
+ "nodeBitsLength": 16,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "behavior": "usid",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "fc00:0:1::/48",
+ "proto": "sharp"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator_usid/expected_locators_3.json b/tests/topotests/srv6_locator_usid/expected_locators_3.json
new file mode 100644
index 0000000..c0eeacc
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_locators_3.json
@@ -0,0 +1,20 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "fc00:0:1::/48",
+ "blockBitsLength": 32,
+ "nodeBitsLength": 16,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "behavior": "usid",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "fc00:0:1::/48",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator_usid/expected_locators_4.json b/tests/topotests/srv6_locator_usid/expected_locators_4.json
new file mode 100644
index 0000000..b1528ff
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_locators_4.json
@@ -0,0 +1,35 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "fc00:0:1::/48",
+ "blockBitsLength": 32,
+ "nodeBitsLength": 16,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "behavior": "usid",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "fc00:0:1::/48",
+ "proto": "system"
+ }
+ ]
+ },
+ {
+ "name": "loc2",
+ "prefix": "fc00:0:2::/48",
+ "blockBitsLength": 32,
+ "nodeBitsLength": 16,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "fc00:0:2::/48",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator_usid/expected_locators_5.json b/tests/topotests/srv6_locator_usid/expected_locators_5.json
new file mode 100644
index 0000000..b6acc23
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_locators_5.json
@@ -0,0 +1,36 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "fc00:0:1::/48",
+ "blockBitsLength": 32,
+ "nodeBitsLength": 16,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "behavior": "usid",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "fc00:0:1::/48",
+ "proto": "system"
+ }
+ ]
+ },
+ {
+ "name": "loc2",
+ "prefix": "fc00:0:2::/48",
+ "blockBitsLength": 32,
+ "nodeBitsLength": 16,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "behavior": "usid",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "fc00:0:2::/48",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator_usid/expected_locators_6.json b/tests/topotests/srv6_locator_usid/expected_locators_6.json
new file mode 100644
index 0000000..b1528ff
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_locators_6.json
@@ -0,0 +1,35 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "fc00:0:1::/48",
+ "blockBitsLength": 32,
+ "nodeBitsLength": 16,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "behavior": "usid",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "fc00:0:1::/48",
+ "proto": "system"
+ }
+ ]
+ },
+ {
+ "name": "loc2",
+ "prefix": "fc00:0:2::/48",
+ "blockBitsLength": 32,
+ "nodeBitsLength": 16,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "fc00:0:2::/48",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator_usid/expected_locators_7.json b/tests/topotests/srv6_locator_usid/expected_locators_7.json
new file mode 100644
index 0000000..e965e02
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_locators_7.json
@@ -0,0 +1,19 @@
+{
+ "locators":[
+ {
+ "name": "loc2",
+ "prefix": "fc00:0:2::/48",
+ "statusUp": true,
+ "blockBitsLength": 32,
+ "nodeBitsLength": 16,
+ "functionBitsLength": 16,
+ "argumentBitsLength": 0,
+ "chunks":[
+ {
+ "prefix": "fc00:0:2::/48",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator_usid/expected_locators_8.json b/tests/topotests/srv6_locator_usid/expected_locators_8.json
new file mode 100644
index 0000000..6e1b993
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/expected_locators_8.json
@@ -0,0 +1,4 @@
+{
+ "locators":[
+ ]
+}
diff --git a/tests/topotests/srv6_locator_usid/r1/setup.sh b/tests/topotests/srv6_locator_usid/r1/setup.sh
new file mode 100644
index 0000000..36ed713
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/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_usid/r1/sharpd.conf b/tests/topotests/srv6_locator_usid/r1/sharpd.conf
new file mode 100644
index 0000000..d460859
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/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_usid/r1/zebra.conf b/tests/topotests/srv6_locator_usid/r1/zebra.conf
new file mode 100644
index 0000000..190e831
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/r1/zebra.conf
@@ -0,0 +1,20 @@
+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 fc00:0:1::/48 block-len 32 node-len 16 func-bits 16
+ behavior usid
+ !
+ !
+ !
+!
diff --git a/tests/topotests/srv6_locator_usid/test_srv6_locator_usid.py b/tests/topotests/srv6_locator_usid/test_srv6_locator_usid.py
new file mode 100755
index 0000000..5418735
--- /dev/null
+++ b/tests/topotests/srv6_locator_usid/test_srv6_locator_usid.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2022, University of Rome Tor Vergata
+# Authored by Carmine Scarpitta <carmine.scarpitta@uniroma2.it>
+#
+
+"""
+test_srv6_locator_usid.py:
+Test for SRv6 Locator uSID 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_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname))
+ )
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+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=3)
+ 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=3)
+ assert result is None, "Failed"
+
+
+def test_srv6_usid_locator_configuration():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears["r1"]
+
+ # FOR DEVELOPER:
+ # If you want to stop some specific line and start interactive shell,
+ # please use tgen.mininet_cli() to start it.
+
+ logger.info("Verify SRv6 Locators instantiated from config file")
+ check_srv6_locator(router, "expected_locators_1.json")
+ check_sharpd_chunk(router, "expected_chunks_1.json")
+
+
+def test_srv6_usid_locator_get_chunk():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears["r1"]
+
+ # FOR DEVELOPER:
+ # If you want to stop some specific line and start interactive shell,
+ # please use tgen.mininet_cli() to start it.
+
+ logger.info("Get chunk for the locator loc1")
+ router.vtysh_cmd("sharp srv6-manager get-locator-chunk loc1")
+ check_srv6_locator(router, "expected_locators_2.json")
+ check_sharpd_chunk(router, "expected_chunks_2.json")
+
+
+def test_srv6_usid_locator_release_chunk():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears["r1"]
+
+ # FOR DEVELOPER:
+ # If you want to stop some specific line and start interactive shell,
+ # please use tgen.mininet_cli() to start it.
+
+ logger.info("Release chunk for the locator loc1")
+ router.vtysh_cmd("sharp srv6-manager release-locator-chunk loc1")
+ check_srv6_locator(router, "expected_locators_3.json")
+ check_sharpd_chunk(router, "expected_chunks_3.json")
+
+
+def test_srv6_usid_locator_create_locator():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears["r1"]
+
+ # FOR DEVELOPER:
+ # If you want to stop some specific line and start interactive shell,
+ # please use tgen.mininet_cli() to start it.
+
+ logger.info("Create an additional SRv6 Locator")
+ router.vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ locator loc2
+ prefix fc00:0:2::/48 block-len 32 node-len 16 func-bits 16
+ """
+ )
+ check_srv6_locator(router, "expected_locators_4.json")
+ check_sharpd_chunk(router, "expected_chunks_4.json")
+
+
+def test_srv6_usid_locator_set_behavior_usid():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears["r1"]
+
+ # FOR DEVELOPER:
+ # If you want to stop some specific line and start interactive shell,
+ # please use tgen.mininet_cli() to start it.
+
+ logger.info("Specify the SRv6 Locator loc2 as a Micro-segment (uSID) Locator")
+ router.vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ locator loc2
+ behavior usid
+ """
+ )
+ check_srv6_locator(router, "expected_locators_5.json")
+ check_sharpd_chunk(router, "expected_chunks_5.json")
+
+
+def test_srv6_usid_locator_unset_behavior_usid():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears["r1"]
+
+ # FOR DEVELOPER:
+ # If you want to stop some specific line and start interactive shell,
+ # please use tgen.mininet_cli() to start it.
+
+ logger.info("Clear Micro-segment (uSID) Locator flag for loc2")
+ router.vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ locator loc2
+ no behavior usid
+ """
+ )
+ check_srv6_locator(router, "expected_locators_6.json")
+ check_sharpd_chunk(router, "expected_chunks_6.json")
+
+
+def test_srv6_usid_locator_delete():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears["r1"]
+
+ # FOR DEVELOPER:
+ # If you want to stop some specific line and start interactive shell,
+ # please use tgen.mininet_cli() to start it.
+
+ logger.info(
+ "Delete locator loc1 and verify that the chunk is released automatically"
+ )
+ router.vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ no locator loc1
+ """
+ )
+ check_srv6_locator(router, "expected_locators_7.json")
+ check_sharpd_chunk(router, "expected_chunks_7.json")
+
+
+def test_srv6_usid_locator_delete_all():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears["r1"]
+
+ # FOR DEVELOPER:
+ # If you want to stop some specific line and start interactive shell,
+ # please use tgen.mininet_cli() to start it.
+
+ logger.info("Delete all the SRv6 configuration")
+ router.vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ no srv6
+ """
+ )
+ check_srv6_locator(router, "expected_locators_8.json")
+ check_sharpd_chunk(router, "expected_chunks_8.json")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/srv6_static_route/__init__.py b/tests/topotests/srv6_static_route/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/srv6_static_route/__init__.py
diff --git a/tests/topotests/srv6_static_route/expected_srv6_route.json b/tests/topotests/srv6_static_route/expected_srv6_route.json
new file mode 100644
index 0000000..45de617
--- /dev/null
+++ b/tests/topotests/srv6_static_route/expected_srv6_route.json
@@ -0,0 +1,49 @@
+{
+ "2001:db8:aaaa::/64": [
+ {
+ "prefix": "2001:db8:aaaa::/64",
+ "prefixLen": 64,
+ "protocol": "static",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "2005::/64": [
+ {
+ "prefix": "2005::/64",
+ "prefixLen": 64,
+ "protocol": "static",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "active": true,
+ "weight": 1,
+ "seg6local": {
+ "action": "unspec"
+ },
+ "seg6": [
+ "2001:db8:aaaa::7",
+ "2002::2",
+ "2003::3",
+ "2004::4"
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_static_route/r1/mgmtd.conf b/tests/topotests/srv6_static_route/r1/mgmtd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/srv6_static_route/r1/mgmtd.conf
diff --git a/tests/topotests/srv6_static_route/r1/setup.sh b/tests/topotests/srv6_static_route/r1/setup.sh
new file mode 100644
index 0000000..36ed713
--- /dev/null
+++ b/tests/topotests/srv6_static_route/r1/setup.sh
@@ -0,0 +1,2 @@
+ip link add dummy0 type dummy
+ip link set dummy0 up
diff --git a/tests/topotests/srv6_static_route/r1/staticd.conf b/tests/topotests/srv6_static_route/r1/staticd.conf
new file mode 100644
index 0000000..a75c69f
--- /dev/null
+++ b/tests/topotests/srv6_static_route/r1/staticd.conf
@@ -0,0 +1,9 @@
+hostname r1
+!
+log stdout notifications
+log monitor notifications
+log commands
+log file staticd.log debugging
+!
+ipv6 route 2001:db8:aaaa::/64 dummy0
+ipv6 route 2005::/64 dummy0 segments 2001:db8:aaaa::7/2002::2/2003::3/2004::4
diff --git a/tests/topotests/srv6_static_route/r1/zebra.conf b/tests/topotests/srv6_static_route/r1/zebra.conf
new file mode 100644
index 0000000..cc70418
--- /dev/null
+++ b/tests/topotests/srv6_static_route/r1/zebra.conf
@@ -0,0 +1,10 @@
+hostname r1
+!
+! debug zebra events
+! debug zebra rib detailed
+!
+log stdout notifications
+log monitor notifications
+log commands
+log file zebra.log debugging
+!
diff --git a/tests/topotests/srv6_static_route/test_srv6_route.py b/tests/topotests/srv6_static_route/test_srv6_route.py
new file mode 100755
index 0000000..7a4cd39
--- /dev/null
+++ b/tests/topotests/srv6_static_route/test_srv6_route.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_srv6_route.py
+#
+# Copyright 2023 6WIND S.A.
+# Dmytro Shytyi <dmytro.shytyi@6wind.com>
+#
+
+"""
+test_srv6_route.py:
+Test for SRv6 static route 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_MGMTD, os.path.join(CWD, "{}/mgmtd.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 test_srv6_static_route():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears["r1"]
+
+ def _check_srv6_static_route(router, expected_route_file):
+ logger.info("checking zebra srv6 static route with multiple segs status")
+ output = json.loads(router.vtysh_cmd("show ipv6 route static json"))
+ expected = open_json_file("{}/{}".format(CWD, expected_route_file))
+ return topotest.json_cmp(output, expected)
+
+ def check_srv6_static_route(router, expected_file):
+ func = functools.partial(_check_srv6_static_route, router, expected_file)
+ success, result = topotest.run_and_expect(func, None, count=15, wait=1)
+ 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("Test for srv6 route configuration")
+ check_srv6_static_route(router, "expected_srv6_route.json")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/static_routing_mpls/r1/zebra.conf b/tests/topotests/static_routing_mpls/r1/zebra.conf
new file mode 100644
index 0000000..4e5d29a
--- /dev/null
+++ b/tests/topotests/static_routing_mpls/r1/zebra.conf
@@ -0,0 +1,16 @@
+hostname r1
+ip forwarding
+ipv6 forwarding
+
+int r1-eth0
+ ip addr 192.168.1.1/24
+!
+int r1-eth1
+ ip addr 192.168.2.1/24
+!
+int lo
+ ip addr 192.0.2.1/32
+!
+int r1-eth1
+ ip addr 192.168.2.1/24
+!
diff --git a/tests/topotests/static_routing_mpls/r2/zebra.conf b/tests/topotests/static_routing_mpls/r2/zebra.conf
new file mode 100644
index 0000000..4e06ae5
--- /dev/null
+++ b/tests/topotests/static_routing_mpls/r2/zebra.conf
@@ -0,0 +1,18 @@
+hostname r2
+ip forwarding
+ipv6 forwarding
+
+int r2-eth0
+ ip addr 192.168.2.2/24
+!
+int r2-eth1
+ mpls enable
+ ip addr 192.168.3.2/24
+!
+int r2-eth2
+ mpls disable
+ ip addr 172.31.3.2/24
+!
+int lo
+ ip addr 192.0.2.2/32
+!
diff --git a/tests/topotests/static_routing_mpls/test_static_routing_mpls.py b/tests/topotests/static_routing_mpls/test_static_routing_mpls.py
new file mode 100644
index 0000000..c1e249c
--- /dev/null
+++ b/tests/topotests/static_routing_mpls/test_static_routing_mpls.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_static_routing_mlpls.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2023 by 6WIND
+#
+
+"""
+test_static_routing_mpls.py: Testing MPLS configuration with mpls interface settings
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+from functools import partial
+import functools
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+ "Build function"
+
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["r1"])
+
+ switch = tgen.add_switch("sw2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("sw3")
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("sw4")
+ switch.add_link(tgen.gears["r2"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+def _populate_mpls_labels():
+ tgen = get_topogen()
+ cmds_list = ["echo 100000 > /proc/sys/net/mpls/platform_labels"]
+ for cmd in cmds_list:
+ for host in ("r1", "r2"):
+ logger.info("input: " + cmd)
+ output = tgen.net[host].cmd(cmd)
+ logger.info("output: " + output)
+
+
+def setup_module(module):
+ "Setup topology"
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ _populate_mpls_labels()
+
+ # 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 _check_mpls_state_interface(router, interface, up=True):
+ output = router.vtysh_cmd("show interface {}".format(interface))
+ if up and "MPLS enabled" in output:
+ return None
+ elif not up and "MPLS enabled" not in output:
+ return None
+ return "not good"
+
+
+def _check_mpls_state(router, interface, configured=True):
+ test_func = functools.partial(
+ _check_mpls_state_interface, router, interface, up=configured
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ return success
+
+
+def test_mpls_configured_on_interface():
+ "Test 'mpls' state is correctly configured on an unconfigured interfaces"
+
+ 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 that MPLS state is on on r2-eth1")
+ assertmsg = "r2, interface r2-eth1, mpls operational state is off, not expected"
+ assert _check_mpls_state(tgen.gears["r2"], "r2-eth1"), assertmsg
+
+ logger.info("Checking that MPLS state is off on r2-eth2")
+ assertmsg = "r2, interface r2-eth2, mpls operational state is on, not expected"
+ assert _check_mpls_state(tgen.gears["r2"], "r2-eth2", False), assertmsg
+
+
+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..46777d7
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py
@@ -0,0 +1,1321 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+"""
+
+ -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 are 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 Expected: Routes should not present in RIB \nError: Routes are 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 are 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 \nExpected: Routes should not present in RIB \nError: Routes are 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 are 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 are 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 \nExpected: Routes should not present in RIB \nError: Routes are 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 \nExpected: Routes should not present in RIB \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 \n Expected: Routes should not present in RIB \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 are 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 \nExpected: Routes should not present in RIB \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 are 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 \nExpected: Routes should not present in RIB \nError: Routes are 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 are 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 \nExpected: Routes should not present in RIB \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 are 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 \nExpected: Routes should not present in RIB \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 are 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 \nExpected: Routes should not present in RIB \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 are 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 \nExpected: Routes should not present in RIB \nError: Routes are 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 \nExpected: Routes should not present in RIB \nError: Routes are 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 are 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 \nExpected: Routes should not present in RIB \nError: Routes are 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 are 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 \nExpected: Routes should not present in RIB \nError: Routes are 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 are 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 are 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 \nExpected: Routes should not present in RIB \nError: Routes are 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 are 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 are 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 \nExpected: Routes should not active in RIB \nError: Routes are 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 \nExpected: Routes should not be active in RIB \nError: Routes are 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 are 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 are 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 \nExpected: Routes should not be active in RIB \nError: Routes are 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 are 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 \nExpected: Routes should not be active in RIB \nError: Routes are 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..e013515
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py
@@ -0,0 +1,1731 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+
+"""
+ -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 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:
+ 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..9d35b7d
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py
@@ -0,0 +1,1346 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+"""
+ -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 are 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 are"
+ " 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 are"
+ " 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 are 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 are 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..2efc0fd
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py
@@ -0,0 +1,976 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+
+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..abae75e
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py
@@ -0,0 +1,1119 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+"""
+
+ -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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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 are 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..820a736
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py
@@ -0,0 +1,2006 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+
+"""
+ -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 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:
+ 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..1ad963f
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py
@@ -0,0 +1,876 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+"""
+ -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 are 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 are"
+ " 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 are"
+ " 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..0378240
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py
@@ -0,0 +1,972 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+
+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/static_simple/r1/mgmtd.conf b/tests/topotests/static_simple/r1/mgmtd.conf
new file mode 100644
index 0000000..0f9f97c
--- /dev/null
+++ b/tests/topotests/static_simple/r1/mgmtd.conf
@@ -0,0 +1 @@
+log timestamp precision 3
diff --git a/tests/topotests/static_simple/r1/staticd.conf b/tests/topotests/static_simple/r1/staticd.conf
new file mode 100644
index 0000000..0f9f97c
--- /dev/null
+++ b/tests/topotests/static_simple/r1/staticd.conf
@@ -0,0 +1 @@
+log timestamp precision 3
diff --git a/tests/topotests/static_simple/r1/zebra.conf b/tests/topotests/static_simple/r1/zebra.conf
new file mode 100644
index 0000000..ec82761
--- /dev/null
+++ b/tests/topotests/static_simple/r1/zebra.conf
@@ -0,0 +1,11 @@
+log timestamp precision 3
+
+interface r1-eth0
+ ip address 101.0.0.1/24
+ ipv6 address 2101::1/64
+exit
+
+interface r1-eth1 vrf red
+ ip address 102.0.0.1/24
+ ipv6 address 2102::1/64
+exit
diff --git a/tests/topotests/static_simple/test_static_simple.py b/tests/topotests/static_simple/test_static_simple.py
new file mode 100644
index 0000000..fd87224
--- /dev/null
+++ b/tests/topotests/static_simple/test_static_simple.py
@@ -0,0 +1,214 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C.
+# Copyright (c) 2019-2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+"""
+Test static route functionality
+"""
+
+import datetime
+import ipaddress
+import math
+import os
+import sys
+import re
+
+import pytest
+from lib.topogen import TopoRouter, Topogen, get_topogen
+from lib.topolog import logger
+from lib.common_config import retry, step
+
+pytestmark = [pytest.mark.staticd]
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ topodef = {"s1": ("r1",), "s2": ("r1",)}
+
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ # Setup VRF red
+ router.net.add_l3vrf("red", 10)
+ router.net.add_loop("lo-red")
+ router.net.attach_iface_to_l3vrf("lo-red", "red")
+ router.net.attach_iface_to_l3vrf(rname + "-eth1", "red")
+ # and select daemons to run
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_MGMTD)
+ router.load_config(TopoRouter.RD_STATIC)
+
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+def get_ip_networks(super_prefix, count):
+ count_log2 = math.log(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 enable_debug(router):
+ router.vtysh_cmd("debug northbound callbacks configuration")
+
+
+def disable_debug(router):
+ router.vtysh_cmd("no debug northbound callbacks configuration")
+
+
+@retry(retry_timeout=30, initial_wait=0.1)
+def check_kernel(r1, super_prefix, count, add, is_blackhole, vrf, matchvia):
+ network = ipaddress.ip_network(super_prefix)
+ vrfstr = f" vrf {vrf}" if vrf else ""
+ if network.version == 6:
+ kernel = r1.run(f"ip -6 route show{vrfstr}")
+ else:
+ kernel = r1.run(f"ip -4 route show{vrfstr}")
+
+ logger.debug("checking kernel routing table%s:\n%s", vrfstr, kernel)
+ for i, net in enumerate(get_ip_networks(super_prefix, count)):
+ if not add:
+ assert str(net) not in kernel
+ continue
+
+ if is_blackhole:
+ route = f"blackhole {str(net)} proto (static|196) metric 20"
+ else:
+ route = (
+ f"{str(net)}(?: nhid [0-9]+)? {matchvia} "
+ "proto (static|196) metric 20"
+ )
+ assert re.search(route, kernel), f"Failed to find \n'{route}'\n in \n'{kernel}'"
+
+
+def do_config(
+ r1,
+ count,
+ add=True,
+ do_ipv6=False,
+ via=None,
+ vrf=None,
+ use_cli=False,
+):
+ optype = "adding" if add else "removing"
+ iptype = "IPv6" if do_ipv6 else "IPv4"
+
+ #
+ # Set the route details
+ #
+
+ if vrf:
+ super_prefix = "2002::/48" if do_ipv6 else "20.0.0.0/8"
+ else:
+ super_prefix = "2001::/48" if do_ipv6 else "10.0.0.0/8"
+
+ matchtype = ""
+ matchvia = ""
+ if via == "blackhole":
+ pass
+ elif via:
+ matchvia = f"dev {via}"
+ else:
+ if vrf:
+ via = "2102::2" if do_ipv6 else "102.0.0.2"
+ matchvia = f"via {via} dev r1-eth1"
+ else:
+ via = "2101::2" if do_ipv6 else "101.0.0.2"
+ matchvia = f"via {via} dev r1-eth0"
+
+ vrfdbg = " in vrf {}".format(vrf) if vrf else ""
+ logger.debug("{} {} static {} routes{}".format(optype, count, iptype, vrfdbg))
+
+ #
+ # Generate config file in a retrievable place
+ #
+
+ config_file = os.path.join(
+ r1.logdir, r1.name, "{}-routes-{}.conf".format(iptype.lower(), optype)
+ )
+ with open(config_file, "w") as f:
+ if use_cli:
+ f.write("configure terminal\n")
+ if vrf:
+ f.write("vrf {}\n".format(vrf))
+
+ for i, net in enumerate(get_ip_networks(super_prefix, count)):
+ if add:
+ f.write("ip route {} {}\n".format(net, via))
+ else:
+ f.write("no ip route {} {}\n".format(net, via))
+
+ #
+ # Load config file.
+ #
+
+ if use_cli:
+ load_command = 'vtysh < "{}"'.format(config_file)
+ else:
+ load_command = 'vtysh -f "{}"'.format(config_file)
+ tstamp = datetime.datetime.now()
+ output = r1.cmd_raises(load_command)
+ delta = (datetime.datetime.now() - tstamp).total_seconds()
+
+ #
+ # Verify the results are in the kernel
+ #
+ check_kernel(r1, super_prefix, count, add, via == "blackhole", vrf, matchvia)
+
+ optyped = "added" if add else "removed"
+ logger.debug(
+ "{} {} {} static routes under {}{} in {}s".format(
+ optyped, count, iptype.lower(), super_prefix, vrfdbg, delta
+ )
+ )
+
+
+def guts(tgen, vrf, use_cli):
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.routers()["r1"]
+
+ step("add via gateway", reset=True)
+ do_config(r1, 1, True, False, vrf=vrf, use_cli=use_cli)
+ step("remove via gateway")
+ do_config(r1, 1, False, False, vrf=vrf, use_cli=use_cli)
+
+ via = f"lo-{vrf}" if vrf else "lo"
+ step("add via loopback")
+ do_config(r1, 1, True, False, via=via, vrf=vrf, use_cli=use_cli)
+ step("remove via loopback")
+ do_config(r1, 1, False, False, via=via, vrf=vrf, use_cli=use_cli)
+
+ step("add via blackhole")
+ do_config(r1, 1, True, False, via="blackhole", vrf=vrf, use_cli=use_cli)
+ step("remove via blackhole")
+ do_config(r1, 1, False, False, via="blackhole", vrf=vrf, use_cli=use_cli)
+
+
+def test_static_cli(tgen):
+ guts(tgen, "", True)
+
+
+def test_static_file(tgen):
+ guts(tgen, "", False)
+
+
+def test_static_vrf_cli(tgen):
+ guts(tgen, "red", True)
+
+
+def test_static_vrf_file(tgen):
+ guts(tgen, "red", False)
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/tc_basic/test_tc_basic.py b/tests/topotests/tc_basic/test_tc_basic.py
new file mode 100755
index 0000000..f64e83c
--- /dev/null
+++ b/tests/topotests/tc_basic/test_tc_basic.py
@@ -0,0 +1,120 @@
+#!/usr/bin/python
+# SPDX-License-Identifier: ISC
+
+#
+# test_tc_basic.py
+#
+# Copyright (c) 2022 by Shichu Yang
+#
+"""
+test_tc_basic.py: Test basic TC filters, classes and qdiscs.
+"""
+import sys
+import os
+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/"))
+
+from lib.topogen import Topogen, TopoRouter
+from lib.topolog import logger
+
+pytestmark = [
+ pytest.mark.sharpd
+]
+
+def build_topo(tgen):
+ "Build function"
+
+ r1 = tgen.add_router("r1")
+ r2 = tgen.add_router("r2")
+
+ # Create a p2p connection between r1 and r2
+ tgen.add_link(r1, r2)
+
+ # Create switches with each router connected to it to simulate a empty network.
+ switch = tgen.add_switch("s1")
+ switch.add_link(r1)
+
+ switch = tgen.add_switch("s2")
+ 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__)
+
+ # ... 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 _, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA)
+ router.load_config(TopoRouter.RD_SHARP)
+
+ # 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")
+
+def fetch_iproute2_tc_info(r, interface):
+ qdisc = r.cmd("tc qdisc show dev %s" % interface)
+ tclass = r.cmd("tc class show dev %s" % interface)
+ tfilter = r.cmd("tc filter show dev %s" % interface)
+ return qdisc, tclass, tfilter
+
+# ===================
+# The tests functions
+# ===================
+
+def test_tc_basic(tgen):
+ "Test installing one pair of filter & class by sharpd"
+
+ r1 = tgen.gears["r1"]
+ intf = "r1-eth0"
+ r1.vtysh_cmd("sharp tc dev %s source 192.168.100.0/24 destination 192.168.101.0/24 ip-protocol tcp src-port 8000 dst-port 8001 rate 20mbit" % intf)
+
+ time.sleep(3)
+
+ qdisc, tclass, tfilter = fetch_iproute2_tc_info(r1, intf)
+
+ logger.info("tc qdisc on %s: %s", intf, qdisc)
+ logger.info("tc class on %s: %s", intf, tclass)
+ logger.info("tc filter on %s: %s", intf, tfilter)
+
+ assert "htb" in qdisc
+ assert "beef:" in qdisc
+
+ assert "20Mbit" in tclass
+
+ assert "tcp" in tfilter
+ assert "dst_ip 192.168.101.0/24" in tfilter
+ assert "src_ip 192.168.100.0/24" in tfilter
+ assert "dst_port 8001" in tfilter
+ assert "src_port 8000" in tfilter
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args)) \ No newline at end of file
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..529520c
--- /dev/null
+++ b/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_zebra_multiple_connected.py
+#
+# Copyright (c) 2022 by
+# Nvidia Corporation
+# Donald Sharp
+#
+
+"""
+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..522c390
--- /dev/null
+++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_zebra_netlink.py
+#
+# Copyright (c) 2020 by
+#
+
+"""
+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")
+
+ entry = {"r1-eth0": {"addresses": ["192.168.1.1/24"]}}
+ ok = topotest.router_json_cmp_retry(r1, "show int brief json", entry, False, 30)
+ assert ok, '"r1" Address not installed yet'
+
+ 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, False, 30)
+ 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..6956ab7
--- /dev/null
+++ b/tests/topotests/zebra_nht_resolution/test_verify_nh_resolution.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2022 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+
+"""
+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..25fbb97
--- /dev/null
+++ b/tests/topotests/zebra_opaque/test_zebra_opaque.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+
+"""
+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_1_vrf_before.json b/tests/topotests/zebra_rib/r1/v4_route_1_vrf_before.json
new file mode 100644
index 0000000..997d12f
--- /dev/null
+++ b/tests/topotests/zebra_rib/r1/v4_route_1_vrf_before.json
@@ -0,0 +1,27 @@
+{
+ "3.5.1.0/24":[
+ {
+ "prefix":"3.5.1.0/24",
+ "prefixLen":24,
+ "protocol":"kernel",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.210.1",
+ "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..05036fa
--- /dev/null
+++ b/tests/topotests/zebra_rib/test_zebra_rib.py
@@ -0,0 +1,372 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# test_zebra_rib.py
+#
+# Copyright (c) 2019 by
+# Cumulus Networks, Inc
+# Donald Sharp
+#
+
+"""
+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"
+ # 8 links to 8 switches on r1
+ topodef = {"s{}".format(x): ("r1",) for x in range(1, 9)}
+ 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_route_vrf():
+ "Test kernel routes should be removed after interface changes vrf"
+ logger.info("Test kernel routes should be removed after interface changes vrf")
+ vrf = "RED"
+ tgen = get_topogen()
+ r1 = tgen.gears["r1"]
+
+ # Add kernel routes, the interface is initially in default vrf
+ r1.run("ip route add 3.5.1.0/24 via 192.168.210.1 dev r1-eth0")
+ json_file = "{}/r1/v4_route_1_vrf_before.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route 3.5.1.0/24 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=5, wait=1)
+ assert result is None, '"r1" JSON output mismatches'
+
+ # Change the interface's vrf
+ r1.run("ip link add {} type vrf table 1".format(vrf))
+ r1.run("ip link set {} up".format(vrf))
+ r1.run("ip link set dev r1-eth0 master {}".format(vrf))
+
+ expected = "{}"
+ test_func = partial(
+ topotest.router_output_cmp, r1, "show ip route 3.5.1.0/24 json", expected
+ )
+ result, diff = topotest.run_and_expect(test_func, "", count=5, wait=1)
+ assertmsg = "{} should not have the kernel route.\n{}".format('"r1"', diff)
+ assert result, assertmsg
+
+ # Clean up
+ r1.run("ip link set dev r1-eth0 nomaster")
+ r1.run("ip link del dev {}".format(vrf))
+
+
+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")
+
+ def check_initial_routes_installed(router):
+ output = json.loads(router.vtysh_cmd("show ip route summ json"))
+ expected = {
+ "routes": [{"type": "static", "rib": 2}, {"type": "sharp", "rib": 500}]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = partial(check_initial_routes_installed, r1)
+ success, result = topotest.run_and_expect(test_func, None, count=40, wait=1)
+
+ 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=10, 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=10, 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..4b462a5
--- /dev/null
+++ b/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_zebra_seg6_route.py
+#
+# Copyright (c) 2020 by
+# LINE Corporation, Hiroki Shirokura <slank.dev@gmail.com>
+#
+
+"""
+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=20, 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..e391922
--- /dev/null
+++ b/tests/topotests/zebra_seg6local_route/r1/routes.json
@@ -0,0 +1,123 @@
+[
+ {
+ "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" }
+ }]
+ }]
+ },
+ {
+ "in": {
+ "dest": "5::1",
+ "context": "End_DT46 10"
+ },
+ "out":[{
+ "prefix":"5::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.DT46" }
+ }]
+ }],
+ "required_kernel": "5.14"
+ }
+]
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..ec27ec9
--- /dev/null
+++ b/tests/topotests/zebra_seg6local_route/r1/setup.sh
@@ -0,0 +1,6 @@
+ip link add dum0 type dummy
+ip link set dum0 up
+ip link add vrf10 type vrf table 10
+ip link set vrf10 up
+sysctl -w net.ipv6.conf.dum0.disable_ipv6=0
+sysctl -w net.vrf.strict_mode=1
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..0dc8774
--- /dev/null
+++ b/tests/topotests/zebra_seg6local_route/test_zebra_seg6local_route.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_zebra_seg6local_route.py
+#
+# Copyright (c) 2020 by
+# LINE Corporation, Hiroki Shirokura <slank.dev@gmail.com>
+#
+
+"""
+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
+from lib.common_config import required_linux_kernel_version
+
+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))
+
+ if manifest.get("required_kernel") is not None:
+ if required_linux_kernel_version(manifest["required_kernel"]) is not True:
+ logger.info(
+ "Kernel requirements are not met. Skipping {} {}".format(
+ dest, context
+ )
+ )
+ continue
+
+ 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))
diff --git a/tests/zebra/subdir.am b/tests/zebra/subdir.am
new file mode 100644
index 0000000..d9fcde8
--- /dev/null
+++ b/tests/zebra/subdir.am
@@ -0,0 +1,17 @@
+if !ZEBRA
+PYTEST_IGNORE += --ignore=zebra/
+endif
+ZEBRA_TEST_LDADD = zebra/label_manager.o $(ALL_TESTS_LDADD)
+
+
+if ZEBRA
+check_PROGRAMS += tests/zebra/test_lm_plugin
+endif
+tests_zebra_test_lm_plugin_CFLAGS = $(TESTS_CFLAGS)
+tests_zebra_test_lm_plugin_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_zebra_test_lm_plugin_LDADD = $(ZEBRA_TEST_LDADD)
+tests_zebra_test_lm_plugin_SOURCES = tests/zebra/test_lm_plugin.c
+EXTRA_DIST += \
+ tests/zebra/test_lm_plugin.py \
+ tests/zebra/test_lm_plugin.refout \
+ # end
diff --git a/tests/zebra/test_lm_plugin.c b/tests/zebra/test_lm_plugin.c
new file mode 100644
index 0000000..9ad0bc4
--- /dev/null
+++ b/tests/zebra/test_lm_plugin.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Label Manager tests.
+ * Copyright (C) 2020 Volta Networks
+ * Patrick Ruddy
+ */
+
+
+#include <zebra.h>
+#include "zebra/zapi_msg.h"
+#include "zebra/label_manager.h"
+
+/* shim out unused functions/variables to allow the lablemanager to compile*/
+DEFINE_KOOH(zserv_client_close, (struct zserv * client), (client));
+unsigned long zebra_debug_packet = 0;
+struct zserv *zserv_find_client_session(uint8_t proto, unsigned short instance,
+ uint32_t session_id)
+{
+ return NULL;
+}
+
+int zsend_label_manager_connect_response(struct zserv *client, vrf_id_t vrf_id,
+ unsigned short result)
+{
+ return 0;
+}
+
+int zsend_assign_label_chunk_response(struct zserv *client, vrf_id_t vrf_id,
+ struct label_manager_chunk *lmc)
+{
+ return 0;
+}
+
+
+static int test_client_connect(struct zserv *client, vrf_id_t vrf_id)
+{
+ return 0;
+}
+
+static int test_client_disconnect(struct zserv *client)
+{
+ return 0;
+}
+
+/* external test hook functions */
+static int lm_get_chunk_pi(struct label_manager_chunk **lmc,
+ struct zserv *client, uint8_t keep, uint32_t size,
+ uint32_t base, vrf_id_t vrf_id)
+{
+ if (base == 0)
+ *lmc = create_label_chunk(10, 55, 0, 1, 50, 50 + size);
+ else
+ *lmc = assign_label_chunk(10, 55, 0, 1, size, base);
+
+ return 0;
+}
+
+static int lm_release_chunk_pi(struct zserv *client, uint32_t start,
+ uint32_t end)
+{
+ return release_label_chunk(client->proto, client->instance,
+ client->session_id, start, end);
+}
+
+
+/* use external allocations */
+static void lp_plugin_init(void)
+{
+ /* register our own hooks */
+ hook_register(lm_client_connect, test_client_connect);
+ hook_register(lm_client_disconnect, test_client_disconnect);
+ hook_register(lm_get_chunk, lm_get_chunk_pi);
+ hook_register(lm_release_chunk, lm_release_chunk_pi);
+}
+
+static void lp_plugin_cleanup(void)
+{
+ /* register our own hooks */
+ hook_unregister(lm_client_connect, test_client_connect);
+ hook_unregister(lm_client_disconnect, test_client_disconnect);
+ hook_unregister(lm_get_chunk, lm_get_chunk_pi);
+ hook_unregister(lm_release_chunk, lm_release_chunk_pi);
+}
+
+
+/* tests */
+
+static void test_lp_plugin(void)
+{
+ struct label_manager_chunk *lmc;
+
+ lmc = assign_label_chunk(10, 55, 0, 1, 50, 0);
+ fprintf(stdout,
+ "chunk: start %u end %u proto %u instance %u session %u keep %s\n",
+ lmc->start, lmc->end, lmc->proto, lmc->instance,
+ lmc->session_id, lmc->keep ? "yes" : "no");
+ delete_label_chunk(lmc);
+
+ lmc = assign_label_chunk(10, 55, 0, 1, 50, 100);
+ fprintf(stdout,
+ "chunk: start %u end %u proto %u instance %u session %u keep %s\n",
+ lmc->start, lmc->end, lmc->proto, lmc->instance,
+ lmc->session_id, lmc->keep ? "yes" : "no");
+ release_label_chunk(10, 55, 0, lmc->start, lmc->end);
+}
+
+int main(int argc, char **argv)
+{
+ /* set up label manager and release it's hooks */
+ label_manager_init();
+ lm_hooks_unregister();
+
+ /* test plugin */
+ lp_plugin_init();
+ test_lp_plugin();
+ lp_plugin_cleanup();
+
+ /* this keeps the compiler happy */
+ hook_call(zserv_client_close, NULL);
+ return 0;
+}
diff --git a/tests/zebra/test_lm_plugin.py b/tests/zebra/test_lm_plugin.py
new file mode 100644
index 0000000..bf4f3ce
--- /dev/null
+++ b/tests/zebra/test_lm_plugin.py
@@ -0,0 +1,5 @@
+import frrtest
+
+
+class TestLmplugin(frrtest.TestRefOut):
+ program = "./test_lm_plugin"
diff --git a/tests/zebra/test_lm_plugin.refout b/tests/zebra/test_lm_plugin.refout
new file mode 100644
index 0000000..35824f1
--- /dev/null
+++ b/tests/zebra/test_lm_plugin.refout
@@ -0,0 +1,2 @@
+chunk: start 16 end 65 proto 10 instance 55 session 0 keep yes
+chunk: start 100 end 149 proto 10 instance 55 session 0 keep yes