summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/.gitignore2
-rw-r--r--tests/bgpd/test_aspath.c3
-rw-r--r--tests/bgpd/test_capability.c2
-rw-r--r--tests/bgpd/test_mp_attr.c2
-rw-r--r--tests/bgpd/test_mpath.c2
-rw-r--r--tests/bgpd/test_packet.c2
-rw-r--r--tests/bgpd/test_peer_attr.c2
-rw-r--r--tests/isisd/test_common.c18
-rw-r--r--tests/isisd/test_common.h3
-rw-r--r--tests/isisd/test_isis_spf.c101
-rw-r--r--tests/isisd/test_isis_spf.in12
-rw-r--r--tests/isisd/test_isis_spf.refout1221
-rw-r--r--tests/lib/test_xref.c140
-rw-r--r--tests/lib/test_xref.py6
-rw-r--r--tests/ospfd/.gitignore3
-rw-r--r--tests/ospfd/common.c248
-rw-r--r--tests/ospfd/common.h47
-rw-r--r--tests/ospfd/test_ospf_spf.c303
-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/subdir.am44
-rw-r--r--tests/topotests/all-protocol-startup/r1/ip_nht.ref12
-rw-r--r--tests/topotests/all-protocol-startup/test_all_protocol_startup.py78
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py5
-rw-r--r--tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py3
-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.conf21
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/ospfd.conf26
-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.conf19
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt2/ospfd.conf24
-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.conf19
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt3/ospfd.conf24
-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.conf18
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt4/ospfd.conf23
-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.conf18
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt5/ospfd.conf23
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt5/zebra.conf22
-rwxr-xr-xtests/topotests/bfd-ospf-topo1/test_bfd_ospf_topo1.py307
-rw-r--r--tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py3
-rw-r--r--tests/topotests/bfd-topo1/test_bfd_topo1.py2
-rw-r--r--tests/topotests/bfd-topo2/test_bfd_topo2.py2
-rw-r--r--tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py2
-rw-r--r--tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py30
-rw-r--r--tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py34
-rw-r--r--tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py34
-rw-r--r--tests/topotests/bgp-evpn-mh/test_evpn_mh.py23
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf1
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json4
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf1
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json4
-rwxr-xr-xtests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py6
-rw-r--r--tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py7
-rw-r--r--tests/topotests/bgp-route-map/test_route_map_topo1.py33
-rw-r--r--tests/topotests/bgp-route-map/test_route_map_topo2.py64
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce1/bgpd.conf12
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce1/snmpd.conf15
-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.conf15
-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.conf15
-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.conf15
-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.conf17
-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.conf15
-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.conf15
-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.conf15
-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-vrf-route-leak-basic/test_bgp-vrf-route-leak-basic.py18
-rw-r--r--tests/topotests/bgp_blackhole_community/__init__.py0
-rw-r--r--tests/topotests/bgp_blackhole_community/r1/bgpd.conf14
-rw-r--r--tests/topotests/bgp_blackhole_community/r1/zebra.conf10
-rw-r--r--tests/topotests/bgp_blackhole_community/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_blackhole_community/r2/zebra.conf12
-rw-r--r--tests/topotests/bgp_blackhole_community/r3/bgpd.conf6
-rw-r--r--tests/topotests/bgp_blackhole_community/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_blackhole_community/r4/bgpd.conf6
-rw-r--r--tests/topotests/bgp_blackhole_community/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py156
-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.py225
-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_features/exabgp.env53
-rw-r--r--tests/topotests/bgp_features/peer1/exa_readpipe.py19
-rw-r--r--tests/topotests/bgp_features/peer1/exabgp.cfg12
-rw-r--r--tests/topotests/bgp_features/peer2/exa_readpipe.py19
-rw-r--r--tests/topotests/bgp_features/peer2/exabgp.cfg12
-rw-r--r--tests/topotests/bgp_features/peer3/exa_readpipe.py19
-rw-r--r--tests/topotests/bgp_features/peer3/exabgp.cfg12
-rw-r--r--tests/topotests/bgp_features/peer4/exa_readpipe.py19
-rw-r--r--tests/topotests/bgp_features/peer4/exabgp.cfg12
-rw-r--r--tests/topotests/bgp_features/r1/bgp_damp_announced.json21
-rw-r--r--tests/topotests/bgp_features/r1/bgp_damp_setup.json10
-rw-r--r--tests/topotests/bgp_features/r2/bgp_damp_announced.json21
-rw-r--r--tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json18
-rw-r--r--tests/topotests/bgp_features/test_bgp_features.py925
-rw-r--r--tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py162
-rw-r--r--tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py31
-rw-r--r--tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py33
-rw-r--r--tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py12
-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.py160
-rw-r--r--tests/topotests/bgp_lu_topo1/R1/bgpd.conf21
-rw-r--r--tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json8
-rw-r--r--tests/topotests/bgp_lu_topo1/R1/zebra.conf6
-rw-r--r--tests/topotests/bgp_lu_topo1/R2/bgpd.conf23
-rw-r--r--tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json8
-rw-r--r--tests/topotests/bgp_lu_topo1/R2/zebra.conf11
-rw-r--r--tests/topotests/bgp_lu_topo1/R3/bgpd.conf523
-rw-r--r--tests/topotests/bgp_lu_topo1/R3/zebra.conf9
-rw-r--r--tests/topotests/bgp_lu_topo1/test_bgp_lu.py178
-rw-r--r--tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py41
-rw-r--r--tests/topotests/bgp_peer-group/__init__.py0
-rw-r--r--tests/topotests/bgp_peer-group/r1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_peer-group/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_peer-group/r2/bgpd.conf7
-rw-r--r--tests/topotests/bgp_peer-group/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_peer-group/r3/bgpd.conf7
-rw-r--r--tests/topotests/bgp_peer-group/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_peer-group/test_bgp_peer-group.py99
-rw-r--r--tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py6
-rw-r--r--tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py53
-rw-r--r--tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py17
-rw-r--r--tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py2
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py7
-rw-r--r--tests/topotests/eigrp-topo1/test_eigrp_topo1.py2
-rw-r--r--tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py2
-rw-r--r--tests/topotests/example-test/test_template.py12
-rwxr-xr-xtests/topotests/example-topojson-test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py13
-rwxr-xr-xtests/topotests/example-topojson-test/test_topo_json_single_link/test_example_topojson.py13
-rwxr-xr-xtests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py13
-rwxr-xr-xtests/topotests/isis-lfa-topo1/test_isis_lfa_topo1.py343
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/__init__.py0
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt1/isisd.conf27
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_ip_route.ref89
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_ipv6_route.ref65
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref32
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt1/step2/show_ip_route.ref82
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt1/step2/show_ipv6_route.ref59
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt1/step3/show_ip_route.ref62
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt1/step3/show_ipv6_route.ref40
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt1/step4/show_ip_route.ref89
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt1/step4/show_ipv6_route.ref65
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt1/zebra.conf19
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt2/isisd.conf35
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_ip_route.ref77
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_ipv6_route.ref40
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref58
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt2/zebra.conf22
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt3/isisd.conf35
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_ip_route.ref97
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_ipv6_route.ref59
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref51
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt3/zebra.conf22
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt4/isisd.conf42
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_ip_route.ref94
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_ipv6_route.ref21
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref63
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt4/zebra.conf25
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt5/isisd.conf42
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_ip_route.ref118
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_ipv6_route.ref40
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref63
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt5/zebra.conf25
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt6/isisd.conf32
-rw-r--r--tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_ip_route.ref107
-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.ref100
-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.ref80
-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.ref107
-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.py353
-rw-r--r--tests/topotests/isis-rlfa-topo1/__init__.py0
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/isisd.conf39
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step1/show_ip_route.ref235
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step1/show_ipv6_route.ref207
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step10/show_ip_route.ref.diff68
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step10/show_ipv6_route.ref.diff62
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step2/show_ip_route.ref.diff134
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step2/show_ipv6_route.ref.diff122
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step3/show_ip_route.ref.diff134
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step3/show_ipv6_route.ref.diff122
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step4/show_ip_route.ref.diff68
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step4/show_ipv6_route.ref.diff62
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step5/show_ip_route.ref.diff68
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step5/show_ipv6_route.ref.diff62
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step6/show_ip_route.ref.diff134
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step6/show_ipv6_route.ref.diff122
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step7/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step7/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step8/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step8/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step9/show_ip_route.ref.diff68
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step9/show_ipv6_route.ref.diff62
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt2/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt2/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt2/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt3/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt3/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt3/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt4/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt4/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt4/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt5/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt5/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt5/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt6/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt6/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt6/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt7/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt7/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt7/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt8/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt8/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt8/zebra.conf22
-rwxr-xr-xtests/topotests/isis-rlfa-topo1/test_isis_rlfa_topo1.py662
-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_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.py532
-rw-r--r--tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py2
-rwxr-xr-xtests/topotests/isis-tilfa-topo1/test_isis_tilfa_topo1.py378
-rw-r--r--tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py2
-rw-r--r--tests/topotests/isis-topo1/test_isis_topo1.py2
-rw-r--r--tests/topotests/ldp-topo1/test_ldp_topo1.py10
-rw-r--r--tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py3
-rw-r--r--tests/topotests/lib/bgp.py109
-rw-r--r--tests/topotests/lib/common_config.py750
-rw-r--r--tests/topotests/lib/lutil.py4
-rw-r--r--tests/topotests/lib/ospf.py247
-rw-r--r--tests/topotests/lib/pim.py3450
-rwxr-xr-xtests/topotests/lib/send_bsr_packet.py58
-rw-r--r--tests/topotests/lib/snmptest.py152
-rwxr-xr-xtests/topotests/lib/test/test_json.py229
-rw-r--r--tests/topotests/lib/topogen.py10
-rw-r--r--tests/topotests/lib/topojson.py38
-rw-r--r--tests/topotests/lib/topotest.py95
-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.py1653
-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.py1115
-rw-r--r--tests/topotests/multicast-pim-sm-topo1/multicast_pim_sm_topo1.json140
-rwxr-xr-xtests/topotests/multicast-pim-sm-topo1/test_multicast_pim_sm_topo1.py1698
-rw-r--r--tests/topotests/multicast-pim-sm-topo2/multicast_pim_sm_topo2.json140
-rwxr-xr-xtests/topotests/multicast-pim-sm-topo2/test_multicast_pim_sm_topo2.py1947
-rw-r--r--tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo3.json140
-rw-r--r--tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo4.json137
-rwxr-xr-xtests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo3.py4609
-rwxr-xr-xtests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo4.py1122
-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.json93
-rwxr-xr-xtests/topotests/multicast-pim-static-rp-topo1/test_multicast_pim_static_rp.py3810
-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.json255
-rw-r--r--tests/topotests/ospf-dual-stack/test_ospf_dual_stack.py152
-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.py242
-rw-r--r--tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py6
-rw-r--r--tests/topotests/ospf-topo1/test_ospf_topo1.py29
-rw-r--r--tests/topotests/ospf6-topo1/r2/ip_6_address.nhg.ref10
-rw-r--r--tests/topotests/ospf6-topo1/r3/ip_6_address.nhg.ref10
-rw-r--r--tests/topotests/ospf6-topo1/r4/ip_6_address.nhg.ref10
-rw-r--r--tests/topotests/ospf6-topo1/test_ospf6_topo1.py8
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_chaos.json166
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_p2mp.json198
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_authentication.py5
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_chaos.py576
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py57
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py39
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_lan.py43
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_nssa.py35
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py416
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py648
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py63
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_single_area.py258
-rw-r--r--tests/topotests/pbr-topo1/test_pbr_topo1.py2
-rw-r--r--tests/topotests/pim-basic/test_pim.py2
-rw-r--r--tests/topotests/pytest.ini23
-rw-r--r--tests/topotests/rip-topo1/test_rip_topo1.py10
-rw-r--r--tests/topotests/ripng-topo1/test_ripng_topo1.py10
-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.conf15
-rw-r--r--tests/topotests/simple-snmp-test/r1/zebra.conf22
-rwxr-xr-xtests/topotests/simple-snmp-test/test_simple_snmp.py152
-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.py1261
-rw-r--r--tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py1711
-rw-r--r--tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py1333
-rw-r--r--tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py994
-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.py1083
-rw-r--r--tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py1974
-rw-r--r--tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py875
-rw-r--r--tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py991
-rw-r--r--tests/topotests/zebra_netlink/test_zebra_netlink.py7
-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/test_zebra_rib.py111
-rw-r--r--tests/zebra/test_lm_plugin.c134
-rw-r--r--tests/zebra/test_lm_plugin.py5
-rw-r--r--tests/zebra/test_lm_plugin.refout2
415 files changed, 56214 insertions, 1619 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
index 5e809a81e6..ca20b0ecac 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -47,7 +47,9 @@
/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 \ No newline at end of file
diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c
index 439891b559..936ffaaad5 100644
--- a/tests/bgpd/test_aspath.c
+++ b/tests/bgpd/test_aspath.c
@@ -1265,7 +1265,8 @@ int main(void)
{
int i = 0;
qobj_init();
- bgp_master_init(thread_master_create(NULL), BGP_SOCKET_SNDBUF_SIZE);
+ bgp_master_init(thread_master_create(NULL), BGP_SOCKET_SNDBUF_SIZE,
+ list_new());
master = bm->master;
bgp_option_set(BGP_OPT_NO_LISTEN);
bgp_attr_init();
diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c
index 1b3f90434b..153b83897d 100644
--- a/tests/bgpd/test_capability.c
+++ b/tests/bgpd/test_capability.c
@@ -912,7 +912,7 @@ int main(void)
qobj_init();
master = thread_master_create(NULL);
- bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
vrf_init(NULL, NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN);
diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c
index 7fabaad7fa..f510760913 100644
--- a/tests/bgpd/test_mp_attr.c
+++ b/tests/bgpd/test_mp_attr.c
@@ -1086,7 +1086,7 @@ int main(void)
cmd_init(0);
bgp_vty_init();
master = thread_master_create("test mp attr");
- bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
vrf_init(NULL, NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN);
bgp_attr_init();
diff --git a/tests/bgpd/test_mpath.c b/tests/bgpd/test_mpath.c
index 520c460f15..92efd4c3d6 100644
--- a/tests/bgpd/test_mpath.c
+++ b/tests/bgpd/test_mpath.c
@@ -393,7 +393,7 @@ static int global_test_init(void)
qobj_init();
master = thread_master_create(NULL);
zclient = zclient_new(master, &zclient_options_default);
- bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
vrf_init(NULL, NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN);
diff --git a/tests/bgpd/test_packet.c b/tests/bgpd/test_packet.c
index d2c093fbea..db5918745d 100644
--- a/tests/bgpd/test_packet.c
+++ b/tests/bgpd/test_packet.c
@@ -59,7 +59,7 @@ int main(int argc, char *argv[])
qobj_init();
bgp_attr_init();
master = thread_master_create(NULL);
- bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
vrf_init(NULL, NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN);
diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c
index 0a15886c10..123d97bc97 100644
--- a/tests/bgpd/test_peer_attr.c
+++ b/tests/bgpd/test_peer_attr.c
@@ -1399,7 +1399,7 @@ static void bgp_startup(void)
master = thread_master_create(NULL);
yang_init(true);
nb_init(master, bgpd_yang_modules, array_size(bgpd_yang_modules), false);
- bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
bgp_option_set(BGP_OPT_NO_LISTEN);
vrf_init(NULL, NULL, NULL, NULL, NULL);
frr_pthread_init();
diff --git a/tests/isisd/test_common.c b/tests/isisd/test_common.c
index 5fa604c749..5b2028ffd4 100644
--- a/tests/isisd/test_common.c
+++ b/tests/isisd/test_common.c
@@ -69,6 +69,24 @@ test_find_adjacency(const struct isis_test_node *tnode, const char *hostname)
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)
diff --git a/tests/isisd/test_common.h b/tests/isisd/test_common.h
index 6fd0d3813e..3359a893ac 100644
--- a/tests/isisd/test_common.h
+++ b/tests/isisd/test_common.h
@@ -70,6 +70,9 @@ 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[]);
diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c
index 36ef93669b..e06944a037 100644
--- a/tests/isisd/test_isis_spf.c
+++ b/tests/isisd/test_isis_spf.c
@@ -31,6 +31,7 @@
#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"
@@ -40,6 +41,7 @@ enum test_type {
TEST_SPF = 1,
TEST_REVERSE_SPF,
TEST_LFA,
+ TEST_RLFA,
TEST_TI_LFA,
};
@@ -105,6 +107,86 @@ static void test_run_lfa(struct vty *vty, const struct isis_topology *topology,
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);
+ 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, false, false);
+ vty_out(vty, "Backup:\n");
+ isis_print_routes(vty, spftree_self, 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,
@@ -242,6 +324,11 @@ static int test_run(struct vty *vty, const struct isis_topology *topology,
&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,
@@ -266,6 +353,7 @@ DEFUN(test_isis, test_isis_cmd,
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>]",
@@ -282,6 +370,11 @@ DEFUN(test_isis, test_isis_cmd,
"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"
@@ -335,6 +428,14 @@ DEFUN(test_isis, test_isis_cmd,
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;
diff --git a/tests/isisd/test_isis_spf.in b/tests/isisd/test_isis_spf.in
index 93e18124e6..f8f65ffdf7 100644
--- a/tests/isisd/test_isis_spf.in
+++ b/tests/isisd/test_isis_spf.in
@@ -31,6 +31,18 @@ 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
diff --git a/tests/isisd/test_isis_spf.refout b/tests/isisd/test_isis_spf.refout
index dced6fb103..024f7256e0 100644
--- a/tests/isisd/test_isis_spf.refout
+++ b/tests/isisd/test_isis_spf.refout
@@ -1807,6 +1807,1227 @@ IS-IS L1 IPv6 routing table:
2001:db8::4/128 60 - 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 40 - rt3 16010
+ - rt6 16010
+ 10.0.255.2/32 30 - rt3 16020
+ - rt6 16020
+ 10.0.255.4/32 20 - 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 50 - 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 40 - 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 30 - 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 70 - rt9 16010
+ 10.0.255.4/32 60 - rt9 16040
+ 10.0.255.5/32 50 - rt9 16050
+ 10.0.255.7/32 50 - rt9 16070
+ 10.0.255.8/32 40 - rt9 16080
+ 10.0.255.10/32 60 - rt9 16100
+ 10.0.255.11/32 50 - 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 50 - rt1 implicit-null
+ - rt3 16010
+ 10.0.255.3/32 50 - rt1 16030
+ - rt3 implicit-null
+ 10.0.255.4/32 80 - rt3 50500/16040
+ 10.0.255.5/32 60 - rt1 16050
+ - rt3 16050
+ 10.0.255.6/32 70 - 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 50 - rt1 implicit-null
+ - rt3 16011
+ 2001:db8::3/128 50 - rt1 16031
+ - rt3 implicit-null
+ 2001:db8::4/128 80 - rt3 50500/16041
+ 2001:db8::5/128 60 - rt1 16051
+ - rt3 16051
+ 2001:db8::6/128 70 - 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
diff --git a/tests/lib/test_xref.c b/tests/lib/test_xref.c
new file mode 100644
index 0000000000..700950de1f
--- /dev/null
+++ b/tests/lib/test_xref.c
@@ -0,0 +1,140 @@
+/*
+ * xref tests
+ * Copyright (C) 2020 David Lamparter for NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#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 0000000000..8c3db3e182
--- /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/ospfd/.gitignore b/tests/ospfd/.gitignore
new file mode 100644
index 0000000000..c659b645db
--- /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 0000000000..eb30c4016e
--- /dev/null
+++ b/tests/ospfd/common.c
@@ -0,0 +1,248 @@
+#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 thread_master *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, 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 0000000000..6d3e63e359
--- /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 thread_master *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/test_ospf_spf.c b/tests/ospfd/test_ospf_spf.c
new file mode 100644
index 0000000000..a85f7e14ec
--- /dev/null
+++ b/tests/ospfd/test_ospf_spf.c
@@ -0,0 +1,303 @@
+#include <zebra.h>
+
+#include "getopt.h"
+#include "thread.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, NULL);
+
+ 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 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();
+
+ /* dryrun true, root_node false */
+ ospf_spf_calculate(area, area->router_lsa_self, new_table, 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,
+ 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();
+ thread_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 thread 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 = thread_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 (thread_fetch(master, &thread))
+ thread_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 0000000000..f1e746745f
--- /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 0000000000..92a1c6a145
--- /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 0000000000..d1e3c7bc65
--- /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 0000000000..2dc611ce96
--- /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/subdir.am b/tests/subdir.am
index 211814c1c3..370e6a49a9 100644
--- a/tests/subdir.am
+++ b/tests/subdir.am
@@ -31,6 +31,14 @@ TESTS_ISISD =
IGNORE_ISISD = --ignore=isisd/
endif
+if OSPFD
+TESTS_OSPFD = \
+ tests/ospfd/test_ospf_spf \
+ # end
+else
+TESTS_OSPFD =
+endif
+
if OSPF6D
TESTS_OSPF6D = \
tests/ospf6d/test_lsdb \
@@ -41,6 +49,16 @@ TESTS_OSPF6D =
IGNORE_OSPF6D = --ignore=ospf6d/
endif
+if ZEBRA
+TESTS_ZEBRA = \
+ tests/zebra/test_lm_plugin \
+ #end
+IGNORE_ZEBRA =
+else
+TESTS_ZEBRA =
+IGNORE_ZEBRA = --ignore=zebra/
+endif
+
clippy_scan += \
tests/lib/cli/test_cli.c \
tests/ospf6d/test_lsdb.c \
@@ -73,6 +91,7 @@ check_PROGRAMS = \
tests/lib/test_ttable \
tests/lib/test_typelist \
tests/lib/test_versioncmp \
+ tests/lib/test_xref \
tests/lib/test_zlog \
tests/lib/test_graph \
tests/lib/cli/test_cli \
@@ -80,7 +99,9 @@ check_PROGRAMS = \
tests/lib/northbound/test_oper_data \
$(TESTS_BGPD) \
$(TESTS_ISISD) \
+ $(TESTS_OSPFD) \
$(TESTS_OSPF6D) \
+ $(TESTS_ZEBRA) \
# end
if ZEROMQ
@@ -115,6 +136,7 @@ noinst_HEADERS += \
tests/lib/cli/common_cli.h \
tests/lib/test_typelist.h \
tests/isisd/test_common.h \
+ tests/ospfd/common.h \
# end
#
@@ -134,7 +156,9 @@ TESTS_CFLAGS = \
ALL_TESTS_LDADD = lib/libfrr.la $(LIBCAP)
BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) -lm
ISISD_TEST_LDADD = isisd/libisis.a $(ALL_TESTS_LDADD)
+OSPFD_TEST_LDADD = ospfd/libfrrospf.a $(ALL_TESTS_LDADD)
OSPF6_TEST_LDADD = ospf6d/libospf6.a $(ALL_TESTS_LDADD)
+ZEBRA_TEST_LDADD = zebra/label_manager.o $(ALL_TESTS_LDADD)
tests_bgpd_test_aspath_CFLAGS = $(TESTS_CFLAGS)
tests_bgpd_test_aspath_CPPFLAGS = $(TESTS_CPPFLAGS)
@@ -201,6 +225,11 @@ 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
+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
+
tests_lib_cxxcompat_CFLAGS = $(TESTS_CFLAGS) $(CXX_COMPAT_CFLAGS) $(WERROR)
tests_lib_cxxcompat_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_cxxcompat_SOURCES = tests/lib/cxxcompat.c
@@ -322,6 +351,10 @@ 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
+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
tests_lib_test_zlog_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_zlog_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_zlog_LDADD = $(ALL_TESTS_LDADD)
@@ -336,6 +369,11 @@ 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
+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/runtests.py \
tests/bgpd/test_aspath.py \
@@ -353,6 +391,9 @@ EXTRA_DIST += \
tests/isisd/test_isis_spf.in \
tests/isisd/test_isis_spf.refout \
tests/isisd/test_isis_vertex_queue.py \
+ tests/ospfd/test_ospf_spf.py \
+ tests/ospfd/test_ospf_spf.in \
+ tests/ospfd/test_ospf_spf.refout \
tests/lib/cli/test_commands.in \
tests/lib/cli/test_commands.py \
tests/lib/cli/test_commands.refout \
@@ -377,12 +418,15 @@ EXTRA_DIST += \
tests/lib/test_ttable.refout \
tests/lib/test_typelist.py \
tests/lib/test_versioncmp.py \
+ tests/lib/test_xref.py \
tests/lib/test_zlog.py \
tests/lib/test_graph.py \
tests/lib/test_graph.refout \
tests/ospf6d/test_lsdb.py \
tests/ospf6d/test_lsdb.in \
tests/ospf6d/test_lsdb.refout \
+ tests/zebra/test_lm_plugin.py \
+ tests/zebra/test_lm_plugin.refout \
# end
.PHONY: tests/tests.xml
diff --git a/tests/topotests/all-protocol-startup/r1/ip_nht.ref b/tests/topotests/all-protocol-startup/r1/ip_nht.ref
index 098e3bf387..1da4da4df5 100644
--- a/tests/topotests/all-protocol-startup/r1/ip_nht.ref
+++ b/tests/topotests/all-protocol-startup/r1/ip_nht.ref
@@ -39,6 +39,18 @@
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
diff --git a/tests/topotests/all-protocol-startup/test_all_protocol_startup.py b/tests/topotests/all-protocol-startup/test_all_protocol_startup.py
index 08378d9b58..5942aca71d 100644
--- a/tests/topotests/all-protocol-startup/test_all_protocol_startup.py
+++ b/tests/topotests/all-protocol-startup/test_all_protocol_startup.py
@@ -83,6 +83,9 @@ class NetworkTopo(Topo):
#####################################################
+@pytest.mark.isis
+@pytest.mark.ospf
+@pytest.mark.rip
def setup_module(module):
global topo, net
global fatal_error
@@ -342,7 +345,7 @@ def test_converge_protocols():
actual = (
net["r%s" % i]
.cmd(
- 'vtysh -c "show ip route" | /usr/bin/tail -n +7 | env LC_ALL=en_US.UTF-8 sort 2> /dev/null'
+ 'vtysh -c "show ip route" | sed -e \'/^Codes: /,/^\s*$/d\' | env LC_ALL=en_US.UTF-8 sort 2> /dev/null'
)
.rstrip()
)
@@ -373,7 +376,7 @@ def test_converge_protocols():
actual = (
net["r%s" % i]
.cmd(
- 'vtysh -c "show ipv6 route" | /usr/bin/tail -n +7 | env LC_ALL=en_US.UTF-8 sort 2> /dev/null'
+ 'vtysh -c "show ipv6 route" | sed -e \'/^Codes: /,/^\s*$/d\' | env LC_ALL=en_US.UTF-8 sort 2> /dev/null'
)
.rstrip()
)
@@ -535,6 +538,51 @@ def test_nexthop_groups():
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"'
+ )
+
+ net["r1"].cmd(
+ 'vtysh -c "sharp install routes 6.6.6.3 nexthop-group infinite-recursive 1"'
+ )
+
+ net["r1"].cmd(
+ 'vtysh -c "sharp install routes 6.6.6.2 nexthop-group infinite-recursive 1"'
+ )
+
+ 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
+ nhg_id = route_get_nhg_id("6.6.6.1/32")
+ output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id)
+
+ dups = re.findall(r"(via 1\.1\.1\.1)", output)
+
+ # 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
+ )
+
##CLI(net)
## Remove all NHG routes
@@ -546,6 +594,8 @@ def test_nexthop_groups():
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():
@@ -1016,6 +1066,7 @@ def test_bgp_ipv6_summary():
# For debugging after starting FRR daemons, uncomment the next line
# CLI(net)
+
def test_nht():
print("\n\n**** Test that nexthop tracking is at least nominally working ****\n")
@@ -1026,13 +1077,16 @@ def test_nht():
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 = net["r%s" % i].cmd('vtysh -c "show ip nht" 2> /dev/null').rstrip()
actual = re.sub(r"fd [0-9][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`")
+ 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)
@@ -1043,19 +1097,23 @@ def test_nht():
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 = net["r%s" % i].cmd('vtysh -c "show ipv6 nht" 2> /dev/null').rstrip()
actual = re.sub(r"fd [0-9][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`")
+ 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
global net
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
index 595132214b..cc1c1e3a0c 100644
--- 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
@@ -64,7 +64,7 @@ class BFDTopo(Topo):
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r3"])
-
+@pytest.mark.bfd
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(BFDTopo, mod.__name__)
@@ -74,7 +74,8 @@ def setup_module(mod):
for rname, router in router_list.items():
router.load_config(
- TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)),
+ 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))
diff --git a/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py b/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py
index 1adfec76d8..23da7ed850 100644
--- a/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py
+++ b/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py
@@ -127,7 +127,8 @@ class TemplateTopo(Topo):
switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
-
+@pytest.mark.bfd
+@pytest.mark.isis
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(TemplateTopo, mod.__name__)
diff --git a/tests/topotests/bfd-ospf-topo1/__init__.py b/tests/topotests/bfd-ospf-topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /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 0000000000..610a20f88a
--- /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 0000000000..18def599b4
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/ospf6d.conf
@@ -0,0 +1,21 @@
+log file ospf6d.log
+log timestamp precision 3
+!
+hostname rt1
+!
+password 1
+!
+interface eth-rt2
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 bfd
+!
+interface eth-rt3
+ 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 0000000000..07b42f9885
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/ospfd.conf
@@ -0,0 +1,26 @@
+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
+!
+interface eth-rt2
+ ip ospf area 0.0.0.0
+ ip ospf bfd
+!
+interface eth-rt3
+ ip ospf area 0.0.0.0
+ ip ospf bfd
+!
+router ospf
+ ospf router-id 1.1.1.1
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ip_route.ref b/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ip_route.ref
new file mode 100644
index 0000000000..f354eff697
--- /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 0000000000..6465efb8b5
--- /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 0000000000..63f0d50784
--- /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 0000000000..42051f9582
--- /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 0000000000..d844ee6813
--- /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 0000000000..32799084fb
--- /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 0000000000..f354eff697
--- /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 0000000000..43eecd0b7a
--- /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 0000000000..409af6308b
--- /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 0000000000..6465efb8b5
--- /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 0000000000..cfb1ef1bb6
--- /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 0000000000..58b44da5c2
--- /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 0000000000..6003125b6b
--- /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 0000000000..437f063d8f
--- /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 0000000000..2f35099564
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt2/ospf6d.conf
@@ -0,0 +1,19 @@
+log file ospf6d.log
+!
+hostname rt2
+!
+password 1
+!
+interface eth-rt1
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 bfd
+!
+interface eth-rt5
+ 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 0000000000..a05d8b58c8
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt2/ospfd.conf
@@ -0,0 +1,24 @@
+log file ospfd.log
+!
+hostname rt2
+!
+password 1
+!
+debug ospf event
+debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-rt1
+ ip ospf area 0.0.0.0
+ ip ospf bfd
+!
+interface eth-rt5
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 2.2.2.2
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt2/step2/show_bfd_peers.ref b/tests/topotests/bfd-ospf-topo1/rt2/step2/show_bfd_peers.ref
new file mode 100644
index 0000000000..d6df1ebfb2
--- /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 0000000000..5fc7fc5b28
--- /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 0000000000..437f063d8f
--- /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 0000000000..3e8777019e
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt3/ospf6d.conf
@@ -0,0 +1,19 @@
+log file ospf6d.log
+!
+hostname rt3
+!
+password 1
+!
+interface eth-rt1
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 bfd
+!
+interface eth-rt4
+ 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 0000000000..1196e6d189
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt3/ospfd.conf
@@ -0,0 +1,24 @@
+log file ospfd.log
+!
+hostname rt3
+!
+password 1
+!
+debug ospf event
+debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-rt1
+ ip ospf area 0.0.0.0
+ ip ospf bfd
+!
+interface eth-rt4
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 3.3.3.3
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt3/step2/show_bfd_peers.ref b/tests/topotests/bfd-ospf-topo1/rt3/step2/show_bfd_peers.ref
new file mode 100644
index 0000000000..d6df1ebfb2
--- /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 0000000000..d368de9bbe
--- /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 0000000000..f35e772790
--- /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 0000000000..bccd1e75bd
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt4/ospf6d.conf
@@ -0,0 +1,18 @@
+log file ospf6d.log
+!
+hostname rt4
+!
+password 1
+!
+interface eth-rt3
+ ipv6 ospf6 network broadcast
+!
+interface eth-rt5
+ 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 0000000000..3a2568b4ab
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt4/ospfd.conf
@@ -0,0 +1,23 @@
+log file ospfd.log
+!
+hostname rt4
+!
+password 1
+!
+debug ospf event
+debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-rt3
+ ip ospf area 0.0.0.0
+!
+interface eth-rt5
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 4.4.4.4
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt4/zebra.conf b/tests/topotests/bfd-ospf-topo1/rt4/zebra.conf
new file mode 100644
index 0000000000..7b053bac35
--- /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 0000000000..f35e772790
--- /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 0000000000..766862276c
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt5/ospf6d.conf
@@ -0,0 +1,18 @@
+log file ospf6d.log
+!
+hostname rt5
+!
+password 1
+!
+interface eth-rt2
+ ipv6 ospf6 network broadcast
+!
+interface eth-rt4
+ ipv6 ospf6 network broadcast
+!
+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 0000000000..a35de5f45f
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt5/ospfd.conf
@@ -0,0 +1,23 @@
+log file ospfd.log
+!
+hostname rt5
+!
+password 1
+!
+debug ospf event
+debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-rt2
+ ip ospf area 0.0.0.0
+!
+interface eth-rt4
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 5.5.5.5
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt5/zebra.conf b/tests/topotests/bfd-ospf-topo1/rt5/zebra.conf
new file mode 100644
index 0000000000..0b7c9e02f3
--- /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 0000000000..1cec62789b
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/test_bfd_ospf_topo1.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_ospf_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bfd_ospf_topo1.py:
+
+ +---------+
+ | |
+ eth-rt2 (.1) | RT1 | eth-rt3 (.1)
+ +----------+ 1.1.1.1 +----------+
+ | | | |
+ | +---------+ |
+ | |
+ | 10.0.2.0/24 |
+ | |
+ | eth-rt1 | (.2)
+ | 10.0.1.0/24 +----+----+
+ | | |
+ | | RT3 |
+ | | 3.3.3.3 |
+ | | |
+ (.2) | eth-rt1 +----+----+
+ +----+----+ eth-rt4 | (.1)
+ | | |
+ | RT2 | |
+ | 2.2.2.2 | 10.0.4.0/24 |
+ | | |
+ +----+----+ |
+ (.1) | eth-rt5 eth-rt3 | (.2)
+ | +----+----+
+ | | |
+ | | RT4 |
+ | | 4.4.4.4 |
+ | | |
+ | +----+----+
+ | 10.0.3.0/24 eth-rt5 | (.1)
+ | |
+ | |
+ | 10.0.5.0/24 |
+ | |
+ | +---------+ |
+ | | | |
+ +----------+ RT5 +----------+
+ eth-rt2 (.2) | 5.5.5.5 | eth-rt4 (.2)
+ | |
+ +---------+
+
+"""
+
+import os
+import sys
+import pytest
+import json
+import re
+from time import sleep
+from time 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
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ #
+ # 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-rt5")
+ switch.add_link(tgen.gears["rt5"], 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["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.iteritems():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_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=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_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.
+ # TODO: add check for array size
+ sleep(2)
+ router_compare_json_output(
+ "rt1", "show ip route ospf json", "step3/show_ip_route_rt2_down.ref", 1, 0
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_rt2_down.ref", 1, 0
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_rt2_down.ref", 1, 0
+ )
+
+ # 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.
+ # TODO: add check for array size
+ sleep(2)
+ router_compare_json_output(
+ "rt1", "show ip route ospf json", "step3/show_ip_route_rt3_down.ref", 1, 0
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_rt3_down.ref", 1, 0
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_rt3_down.ref", 1, 0
+ )
+
+ # 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/test_bfd_profiles_topo1.py b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py
index bd3b876eeb..6283f03ddf 100644
--- a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py
+++ b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py
@@ -77,7 +77,8 @@ class BFDProfTopo(Topo):
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r6"])
-
+@pytest.mark.bfd
+@pytest.mark.isis
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(BFDProfTopo, mod.__name__)
diff --git a/tests/topotests/bfd-topo1/test_bfd_topo1.py b/tests/topotests/bfd-topo1/test_bfd_topo1.py
index 6e589d55eb..4c13fdcfc5 100644
--- a/tests/topotests/bfd-topo1/test_bfd_topo1.py
+++ b/tests/topotests/bfd-topo1/test_bfd_topo1.py
@@ -69,7 +69,7 @@ class BFDTopo(Topo):
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r4"])
-
+@pytest.mark.bfd
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(BFDTopo, mod.__name__)
diff --git a/tests/topotests/bfd-topo2/test_bfd_topo2.py b/tests/topotests/bfd-topo2/test_bfd_topo2.py
index feb4576bd3..5181a40f47 100644
--- a/tests/topotests/bfd-topo2/test_bfd_topo2.py
+++ b/tests/topotests/bfd-topo2/test_bfd_topo2.py
@@ -70,7 +70,7 @@ class BFDTopo(Topo):
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r4"])
-
+@pytest.mark.bfd
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(BFDTopo, mod.__name__)
diff --git a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py
index 5fed135f8d..956b526583 100644
--- a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py
+++ b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py
@@ -70,7 +70,7 @@ class BFDTopo(Topo):
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r4"])
-
+@pytest.mark.bfd
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(BFDTopo, mod.__name__)
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
index b3b7256ac4..b701a0d61e 100644
--- a/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py
+++ b/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py
@@ -282,10 +282,26 @@ def test_BGP_config_with_invalid_ASN_p2(request):
# 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,}},
+ "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)
try:
@@ -819,7 +835,11 @@ def test_bgp_with_loopback_interface(request):
# Adding ['source_link'] = 'lo' key:value pair
topo["routers"][routerN]["bgp"]["address_family"]["ipv4"]["unicast"][
"neighbor"
- ][bgp_neighbor]["dest_link"] = {"lo": {"source_link": "lo",}}
+ ][bgp_neighbor]["dest_link"] = {
+ "lo": {
+ "source_link": "lo",
+ }
+ }
# Creating configuration from JSON
build_config_from_json(tgen, topo)
diff --git a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py
index 54a3c699f3..353df0684b 100644
--- a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py
+++ b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py
@@ -273,8 +273,20 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type):
"r3": {
"bgp": {
"address_family": {
- "ipv4": {"unicast": {"maximum_paths": {"ebgp": ecmp_num,}}},
- "ipv6": {"unicast": {"maximum_paths": {"ebgp": ecmp_num,}}},
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": ecmp_num,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": ecmp_num,
+ }
+ }
+ },
}
}
}
@@ -303,7 +315,7 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type):
input_dict_1,
next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)],
protocol=protocol,
- count_only=True
+ count_only=True,
)
assert result is True, "Testcase {} : Failed \n Error: {}".format(
@@ -312,9 +324,9 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type):
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, test_type):
+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
@@ -337,7 +349,7 @@ def test_ecmp_after_clear_bgp(request, test_type):
addr_type,
dut,
input_dict_1,
- next_hop=NEXT_HOPS[addr_type],
+ next_hop=NEXT_HOPS[addr_type][:int(ecmp_num)],
protocol=protocol,
)
assert result is True, "Testcase {} : Failed \n Error: {}".format(
@@ -360,7 +372,7 @@ def test_ecmp_after_clear_bgp(request, test_type):
addr_type,
dut,
input_dict_1,
- next_hop=NEXT_HOPS[addr_type],
+ next_hop=NEXT_HOPS[addr_type][:int(ecmp_num)],
protocol=protocol,
)
assert result is True, "Testcase {} : Failed \n Error: {}".format(
@@ -371,8 +383,8 @@ def test_ecmp_after_clear_bgp(request, test_type):
def test_ecmp_remove_redistribute_static(request):
- """ Verify routes are cleared from BGP and RIB table of DUT when
- redistribute static configuration is removed."""
+ """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)
@@ -481,8 +493,8 @@ def test_ecmp_remove_redistribute_static(request):
@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"])
def test_ecmp_shut_bgp_neighbor(request, test_type):
- """ Shut BGP neigbors one by one and verify BGP and routing table updated
- accordingly in DUT """
+ """Shut BGP neigbors one by one and verify BGP and routing table updated
+ accordingly in DUT"""
tc_name = request.node.name
write_test_header(tc_name)
diff --git a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py
index 73724ac069..2f73bdb1b8 100644
--- a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py
+++ b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py
@@ -274,8 +274,20 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type):
"r3": {
"bgp": {
"address_family": {
- "ipv4": {"unicast": {"maximum_paths": {"ibgp": ecmp_num,}}},
- "ipv6": {"unicast": {"maximum_paths": {"ibgp": ecmp_num,}}},
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ibgp": ecmp_num,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ibgp": ecmp_num,
+ }
+ }
+ },
}
}
}
@@ -304,7 +316,7 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type):
input_dict_1,
next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)],
protocol=protocol,
- count_only=True
+ count_only=True,
)
assert result is True, "Testcase {} : Failed \n Error: {}".format(
@@ -313,9 +325,9 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type):
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, test_type):
+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
@@ -338,7 +350,7 @@ def test_ecmp_after_clear_bgp(request, test_type):
addr_type,
dut,
input_dict_1,
- next_hop=NEXT_HOPS[addr_type],
+ next_hop=NEXT_HOPS[addr_type][:int(ecmp_num)],
protocol=protocol,
)
assert result is True, "Testcase {} : Failed \n Error: {}".format(
@@ -361,7 +373,7 @@ def test_ecmp_after_clear_bgp(request, test_type):
addr_type,
dut,
input_dict_1,
- next_hop=NEXT_HOPS[addr_type],
+ next_hop=NEXT_HOPS[addr_type][:int(ecmp_num)],
protocol=protocol,
)
assert result is True, "Testcase {} : Failed \n Error: {}".format(
@@ -372,8 +384,8 @@ def test_ecmp_after_clear_bgp(request, test_type):
def test_ecmp_remove_redistribute_static(request):
- """ Verify routes are cleared from BGP and RIB table of DUT when
- redistribute static configuration is removed."""
+ """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)
@@ -482,8 +494,8 @@ def test_ecmp_remove_redistribute_static(request):
@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"])
def test_ecmp_shut_bgp_neighbor(request, test_type):
- """ Shut BGP neigbors one by one and verify BGP and routing table updated
- accordingly in DUT """
+ """Shut BGP neigbors one by one and verify BGP and routing table updated
+ accordingly in DUT"""
tc_name = request.node.name
write_test_header(tc_name)
diff --git a/tests/topotests/bgp-evpn-mh/test_evpn_mh.py b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py
index 4c56d1a02d..61be947a71 100644
--- a/tests/topotests/bgp-evpn-mh/test_evpn_mh.py
+++ b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py
@@ -35,6 +35,8 @@ import json
import platform
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, "../"))
@@ -658,10 +660,11 @@ def test_evpn_mac():
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)
@@ -676,12 +679,13 @@ def check_df_role(dut, esi, role):
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()
@@ -720,10 +724,11 @@ def test_evpn_df():
# 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")
@@ -739,13 +744,14 @@ def check_protodown_rc(dut, protodown_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()
@@ -778,6 +784,7 @@ def test_evpn_uplink_tracking():
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-vxlan_topo1/PE1/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf
index c7a76a98ed..991a1e7e56 100644
--- a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf
@@ -8,3 +8,4 @@ router bgp 65000
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
index 2937504244..e500a1d85c 100644
--- a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json
@@ -6,8 +6,8 @@
"vtepIp":"10.10.10.10",
"mcastGroup":"0.0.0.0",
"advertiseGatewayMacip":"No",
- "numMacs":5,
- "numArpNd":3,
+ "numMacs":6,
+ "numArpNd":6,
"numRemoteVteps":[
"10.30.30.30"
]
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf
index 0a24158bb8..52f8687bc1 100644
--- a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf
@@ -9,3 +9,4 @@ router bgp 65000
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
index 0853147a00..0a56a235bd 100644
--- a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json
@@ -6,8 +6,8 @@
"vtepIp":"10.30.30.30",
"mcastGroup":"0.0.0.0",
"advertiseGatewayMacip":"No",
- "numMacs":5,
- "numArpNd":3,
+ "numMacs":6,
+ "numArpNd":6,
"numRemoteVteps":[
"10.10.10.10"
]
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py
index 5098808d55..9a38158b2b 100755
--- a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py
@@ -310,8 +310,10 @@ def ip_learn_test(tgen, host, local, remote, ip_addr):
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
+ assertmsg = (
+ "learned address mismatch with configured address host: {} learned: {}".format(
+ ip_addr, learned_ip
+ )
)
assert ip_addr == learned_ip, assertmsg
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
index 607b036c6a..a9541a55c5 100644
--- a/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py
+++ b/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py
@@ -238,9 +238,10 @@ def test_next_hop_attribute(request):
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)
+ 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
diff --git a/tests/topotests/bgp-route-map/test_route_map_topo1.py b/tests/topotests/bgp-route-map/test_route_map_topo1.py
index 1aa951edaa..0158e24d31 100644
--- a/tests/topotests/bgp-route-map/test_route_map_topo1.py
+++ b/tests/topotests/bgp-route-map/test_route_map_topo1.py
@@ -807,7 +807,9 @@ def test_route_map_multiple_seq_different_match_set_clause_p0(request):
"prefix_lists": "pf_list_2_{}".format(addr_type)
}
},
- "set": {"locPrf": 150,},
+ "set": {
+ "locPrf": 150,
+ },
},
{
"action": "permit",
@@ -906,7 +908,17 @@ def test_route_map_multiple_seq_different_match_set_clause_p0(request):
dut = "r3"
protocol = "bgp"
input_dict = {
- "r3": {"route_maps": {"rmap_match_pf_list1": [{"set": {"metric": 50,}}],}}
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_list1": [
+ {
+ "set": {
+ "metric": 50,
+ }
+ }
+ ],
+ }
+ }
}
static_routes = [NETWORK[adt][0]]
@@ -1093,7 +1105,14 @@ def test_route_map_set_only_no_match_p0(request):
input_dict_4 = {
"r3": {
"route_maps": {
- "rmap_match_pf_1": [{"action": "permit", "set": {"metric": 50,}}]
+ "rmap_match_pf_1": [
+ {
+ "action": "permit",
+ "set": {
+ "metric": 50,
+ },
+ }
+ ]
}
}
}
@@ -1210,7 +1229,13 @@ def test_route_map_match_only_no_set_p0(request):
"r1": {
"route_maps": {
"rmap_match_pf_1_{}".format(addr_type): [
- {"action": "permit", "set": {"metric": 50, "locPrf": 150,}}
+ {
+ "action": "permit",
+ "set": {
+ "metric": 50,
+ "locPrf": 150,
+ },
+ }
]
}
}
diff --git a/tests/topotests/bgp-route-map/test_route_map_topo2.py b/tests/topotests/bgp-route-map/test_route_map_topo2.py
index 3056aa29f3..958eceba62 100644
--- a/tests/topotests/bgp-route-map/test_route_map_topo2.py
+++ b/tests/topotests/bgp-route-map/test_route_map_topo2.py
@@ -268,12 +268,20 @@ def test_rmap_match_prefix_list_permit_in_and_outbound_prefixes_p0():
"prefix_lists": {
"ipv4": {
"pf_list_1_ipv4": [
- {"seqid": 10, "network": "any", "action": "permit",}
+ {
+ "seqid": 10,
+ "network": "any",
+ "action": "permit",
+ }
]
},
"ipv6": {
"pf_list_1_ipv6": [
- {"seqid": 10, "network": "any", "action": "permit",}
+ {
+ "seqid": 10,
+ "network": "any",
+ "action": "permit",
+ }
]
},
}
@@ -472,7 +480,11 @@ def test_modify_set_match_clauses_in_rmap_p0():
"prefix_lists": {
"ipv4": {
"pf_list_1_ipv4": [
- {"seqid": 10, "network": "any", "action": "permit",}
+ {
+ "seqid": 10,
+ "network": "any",
+ "action": "permit",
+ }
],
"pf_list_2_ipv4": [
{"seqid": 10, "network": "any", "action": "permit"}
@@ -480,7 +492,11 @@ def test_modify_set_match_clauses_in_rmap_p0():
},
"ipv6": {
"pf_list_1_ipv6": [
- {"seqid": 10, "network": "any", "action": "permit",}
+ {
+ "seqid": 10,
+ "network": "any",
+ "action": "permit",
+ }
],
"pf_list_2_ipv6": [
{"seqid": 10, "network": "any", "action": "permit"}
@@ -506,7 +522,9 @@ def test_modify_set_match_clauses_in_rmap_p0():
"prefix_lists": "pf_list_1_{}".format(addr_type)
}
},
- "set": {"locPrf": 150,},
+ "set": {
+ "locPrf": 150,
+ },
}
],
"rmap_match_pf_2_{}".format(addr_type): [
@@ -666,7 +684,9 @@ def test_modify_set_match_clauses_in_rmap_p0():
"prefix_lists": "pf_list_1_{}".format(addr_type)
}
},
- "set": {"locPrf": 1000,},
+ "set": {
+ "locPrf": 1000,
+ },
}
],
"rmap_match_pf_2_{}".format(addr_type): [
@@ -816,12 +836,20 @@ def test_modify_prefix_list_referenced_by_rmap_p0():
"prefix_lists": {
"ipv4": {
"pf_list_1_ipv4": [
- {"seqid": 10, "network": "any", "action": "permit",}
+ {
+ "seqid": 10,
+ "network": "any",
+ "action": "permit",
+ }
]
},
"ipv6": {
"pf_list_1_ipv6": [
- {"seqid": 100, "network": "any", "action": "permit",}
+ {
+ "seqid": 100,
+ "network": "any",
+ "action": "permit",
+ }
]
},
}
@@ -1090,7 +1118,9 @@ def test_remove_prefix_list_referenced_by_rmap_p0():
"prefix_lists": "pf_list_1_{}".format(addr_type)
}
},
- "set": {"locPrf": 150,},
+ "set": {
+ "locPrf": 150,
+ },
}
],
"rmap_match_pf_2_{}".format(addr_type): [
@@ -1894,7 +1924,9 @@ def test_multiple_match_statement_in_route_map_logical_ANDed_p1():
"prefix_lists": "pf_list_1_{}".format(addr_type)
}
},
- "set": {"locPrf": 150,},
+ "set": {
+ "locPrf": 150,
+ },
}
]
}
@@ -1921,7 +1953,9 @@ def test_multiple_match_statement_in_route_map_logical_ANDed_p1():
}
}
},
- "set": {"locPrf": 150,},
+ "set": {
+ "locPrf": 150,
+ },
}
]
}
@@ -2048,7 +2082,9 @@ def test_add_remove_rmap_to_specific_neighbor_p0():
"prefix_lists": "pf_list_1_{}".format(addr_type)
}
},
- "set": {"locPrf": 150,},
+ "set": {
+ "locPrf": 150,
+ },
}
]
}
@@ -3505,7 +3541,9 @@ def test_create_rmap_match_prefix_list_to_deny_in_and_outbound_prefixes_p0():
"prefix_lists": "pf_list_1_{}".format(addr_type)
}
},
- "set": {"locPrf": 150,},
+ "set": {
+ "locPrf": 150,
+ },
}
],
"rmap_match_pf_2_{}".format(addr_type): [
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 0000000000..b598666dfb
--- /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 0000000000..36218d3538
--- /dev/null
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce1/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:10.5.5.5:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
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 0000000000..8ad2ddc48c
--- /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 0000000000..e388ccba8a
--- /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 0000000000..714585cb9b
--- /dev/null
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce2/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:10.6.6.6:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
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 0000000000..fa2e968e55
--- /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 0000000000..e388ccba8a
--- /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 0000000000..36218d3538
--- /dev/null
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce3/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:10.5.5.5:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
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 0000000000..ea91e21bad
--- /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 0000000000..e388ccba8a
--- /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 0000000000..36218d3538
--- /dev/null
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce4/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:10.5.5.5:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
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 0000000000..0866fa9759
--- /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 0000000000..098e55d0ed
--- /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 0000000000..b5ca993da3
--- /dev/null
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/r1/isisd.conf
@@ -0,0 +1,46 @@
+log stdout debugging
+!
+debug isis route-events
+debug isis events
+!
+interface r1-eth0
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r1-eth1
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r1-eth2
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface lo
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ isis passive
+ no isis hello padding
+!
+router isis ISIS1
+ net 01.1111.0000.0000.0001.00
+ is-type level-1
+ topology ipv6-unicast
+!
+line vty
+!
diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r1/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r1/snmpd.conf
new file mode 100644
index 0000000000..c903c1ad2e
--- /dev/null
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/r1/snmpd.conf
@@ -0,0 +1,17 @@
+agentAddress udp:10.1.1.1:161
+
+com2sec public 10.1.1.1 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+noRangeCheck yes \ No newline at end of file
diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r1/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r1/zebra.conf
new file mode 100644
index 0000000000..7228ae6bd2
--- /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 0000000000..3dfa43831a
--- /dev/null
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/r2/isisd.conf
@@ -0,0 +1,37 @@
+log stdout debugging
+!
+debug isis route-events
+debug isis events
+!
+interface r2-eth0
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r2-eth1
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface lo
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ isis passive
+ no isis hello padding
+!
+router isis ISIS1
+ net 01.1111.0000.0000.0002.00
+ is-type level-1
+ topology ipv6-unicast
+!
+line vty
+!
diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r2/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r2/snmpd.conf
new file mode 100644
index 0000000000..0cfebc7238
--- /dev/null
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/r2/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:10.2.2.2:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
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 0000000000..9bc4331bae
--- /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 0000000000..578ebafad6
--- /dev/null
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/r3/isisd.conf
@@ -0,0 +1,45 @@
+log stdout debugging
+!
+debug isis route-events
+debug isis events
+!
+interface r3-eth0
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r3-eth1
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r3-eth2
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface lo
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ isis passive
+ no isis hello padding
+!
+router isis ISIS1
+ net 01.1111.0000.0000.0003.00
+ is-type level-1
+ topology ipv6-unicast
+!
+line vty
diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r3/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r3/snmpd.conf
new file mode 100644
index 0000000000..b9eb00ea52
--- /dev/null
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/r3/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:10.3.3.3:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
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 0000000000..4d2007e787
--- /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 0000000000..2a834c799e
--- /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 0000000000..3e9e9af45f
--- /dev/null
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/r4/isisd.conf
@@ -0,0 +1,36 @@
+log stdout debugging
+!
+debug isis route-events
+debug isis events
+!
+interface r4-eth0
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r4-eth1
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface lo
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ isis passive
+ no isis hello padding
+!
+router isis ISIS1
+ net 01.1111.0000.0000.0004.00
+ is-type level-1
+ topology ipv6-unicast
+!
+line vty
diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r4/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r4/snmpd.conf
new file mode 100644
index 0000000000..ec35f9f9c9
--- /dev/null
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/r4/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:10.4.4.4:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
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 0000000000..c48407c108
--- /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 0000000000..5eb1738632
--- /dev/null
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py
@@ -0,0 +1,738 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_snmp_mplsl3vpn.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_snmp_mplsl3vpn.py: Test mplsL3Vpn MIB [RFC4382].
+"""
+
+import os
+import sys
+import json
+from functools import partial
+from time import sleep
+import pytest
+import re
+
+# 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.
+from mininet.topo import Topo
+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ # 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(TemplateTopo, 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")
+ r2.run("sysctl -w net.mpls.conf.r1-eth0.input=1")
+ r2.run("sysctl -w net.mpls.conf.r1-eth1.input=1")
+ r3.run("sysctl -w net.mpls.conf.r1-eth0.input=1")
+ r3.run("sysctl -w net.mpls.conf.r1-eth1.input=1")
+ r3.run("sysctl -w net.mpls.conf.r1-eth2.input=1")
+ r4.run("sysctl -w net.mpls.conf.r1-eth0.input=1")
+ r4.run("sysctl -w net.mpls.conf.r1-eth1.input=1")
+
+ 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_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.net.get("r1")
+ r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c")
+
+ assertmsg = "BGP SNMP does not seem to be running"
+ assert r1_snmp.test_oid("bgpVersion", "10"), assertmsg
+ 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.net.get("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.net.get("r1")
+ r1_cmd = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c")
+
+ r1_cmd.vtysh_cmd("conf t\ninterface r1-eth3\nshutdown")
+ r1_cmd.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_cmd.vtysh_cmd("conf t\ninterface r1-eth3\nno shutdown")
+ r1_cmd.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.net.get("r1")
+ r1r = tgen.gears["r1"]
+
+ r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c")
+
+ # tgen.mininet_cli()
+ eth3_ifindex = router_interface_get_ifindex(r1r, "eth3")
+ eth4_ifindex = router_interface_get_ifindex(r1r, "eth4")
+ eth5_ifindex = router_interface_get_ifindex(r1r, "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.net.get("r1")
+ r1r = 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)
+ r1r.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
+ r1r.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.net.get("r1")
+ r1r = 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.net.get("r1")
+ r1r = 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",
+ '""',
+ ],
+ "mplsL3VpnVrfRteInetCidrIfIndex": ["5", "6", "4", "5", "0", "6", "0"],
+ "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.net.get("r1")
+ r1r = 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
+ 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-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
index 36f1d8cd56..71f64e9b70 100644
--- 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
@@ -90,7 +90,11 @@ def test_vrf_route_leak():
# Test DONNA VRF.
expect = {
- "10.0.0.0/24": [{"protocol": "connected",}],
+ "10.0.0.0/24": [
+ {
+ "protocol": "connected",
+ }
+ ],
"10.0.1.0/24": [
{"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]}
],
@@ -111,11 +115,19 @@ def test_vrf_route_leak():
"10.0.0.0/24": [
{"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]}
],
- "10.0.1.0/24": [{"protocol": "connected",}],
+ "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",}],
+ "10.0.3.0/24": [
+ {
+ "protocol": "connected",
+ }
+ ],
}
test_func = partial(
diff --git a/tests/topotests/bgp_blackhole_community/__init__.py b/tests/topotests/bgp_blackhole_community/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /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 0000000000..574d199aeb
--- /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 0000000000..70dc5e516d
--- /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 0000000000..2260613253
--- /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 0000000000..cf6fb6d984
--- /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 0000000000..d0c4b400f7
--- /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 0000000000..05ab56d6f1
--- /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 0000000000..0ac963e642
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r4/bgpd.conf
@@ -0,0 +1,6 @@
+!
+router bgp 65002
+ timers bgp 3 9
+ no bgp ebgp-requires-policy
+ neighbor r4-eth0 interface remote-as internal
+!
diff --git a/tests/topotests/bgp_blackhole_community/r4/zebra.conf b/tests/topotests/bgp_blackhole_community/r4/zebra.conf
new file mode 100644
index 0000000000..e2ccaed52a
--- /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 0000000000..a856c9278f
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Test if 172.16.255.254/32 tagged with BLACKHOLE community is not
+re-advertised downstream outside local AS.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from mininet.topo import Topo
+from lib.common_config import step
+
+
+class TemplateTopo(Topo):
+ def build(self, *_args, **_opts):
+ tgen = get_topogen(self)
+
+ 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(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_blackhole_community():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge():
+ output = json.loads(
+ tgen.gears["r2"].vtysh_cmd("show ip bgp 172.16.255.254/32 json")
+ )
+ expected = {"paths": [{"community": {"list": ["blackhole", "noExport"]}}]}
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_no_advertise_ebgp():
+ output = json.loads(
+ tgen.gears["r2"].vtysh_cmd(
+ "show ip bgp neighbor r2-eth1 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {},
+ "totalPrefixCounter": 0,
+ "filteredPrefixCounter": 0,
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_no_advertise_ibgp():
+ output = json.loads(
+ tgen.gears["r2"].vtysh_cmd(
+ "show ip bgp neighbor r2-eth2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {"172.16.255.254/32": {}},
+ "totalPrefixCounter": 2,
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r2"])
+
+ step("Check if 172.16.255.254/32 is not advertised to eBGP peers")
+
+ test_func = functools.partial(_bgp_no_advertise_ebgp)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert (
+ result is None
+ ), 'Advertised blackhole tagged prefix to eBGP peers in "{}"'.format(
+ tgen.gears["r2"]
+ )
+
+ step("Check if 172.16.255.254/32 is advertised to iBGP peers")
+
+ test_func = functools.partial(_bgp_no_advertise_ibgp)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert (
+ result is None
+ ), 'Withdrawn blackhole tagged prefix to iBGP peers in "{}"'.format(
+ tgen.gears["r2"]
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_community_change_update/__init__.py b/tests/topotests/bgp_community_change_update/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /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 0000000000..24cf9dffb9
--- /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 0000000000..e3dbbc051d
--- /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 0000000000..5fc4310266
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py
@@ -0,0 +1,225 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+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.topolog import logger
+from mininet.topo import Topo
+
+from lib.common_config import step
+from time import sleep
+
+
+class TemplateTopo(Topo):
+ def build(self, *_args, **_opts):
+ tgen = get_topogen(self)
+
+ 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(TemplateTopo, 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 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 0000000000..8d7bcb948b
--- /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 0000000000..8b7c03ffbf
--- /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 0000000000..a010085835
--- /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 0000000000..4096f2a2e3
--- /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 0000000000..27f1e81f7d
--- /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 0000000000..7e9458a45c
--- /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 0000000000..8e57284929
--- /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 0000000000..93dd87dbd2
--- /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 0000000000..2f470f18b8
--- /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 0000000000..7f6bbb66b3
--- /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_features/exabgp.env b/tests/topotests/bgp_features/exabgp.env
new file mode 100644
index 0000000000..6c554f5fa8
--- /dev/null
+++ b/tests/topotests/bgp_features/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_features/peer1/exa_readpipe.py b/tests/topotests/bgp_features/peer1/exa_readpipe.py
new file mode 100644
index 0000000000..dba1536388
--- /dev/null
+++ b/tests/topotests/bgp_features/peer1/exa_readpipe.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+"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_features/peer1/exabgp.cfg b/tests/topotests/bgp_features/peer1/exabgp.cfg
new file mode 100644
index 0000000000..2e95252cf6
--- /dev/null
+++ b/tests/topotests/bgp_features/peer1/exabgp.cfg
@@ -0,0 +1,12 @@
+group exabgp {
+ process announce-routes {
+ run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer1.in";
+ encoder text;
+ }
+ neighbor 192.168.101.1 {
+ router-id 192.168.101.3;
+ local-address 192.168.101.3;
+ local-as 65403;
+ peer-as 65000;
+ }
+}
diff --git a/tests/topotests/bgp_features/peer2/exa_readpipe.py b/tests/topotests/bgp_features/peer2/exa_readpipe.py
new file mode 100644
index 0000000000..dba1536388
--- /dev/null
+++ b/tests/topotests/bgp_features/peer2/exa_readpipe.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+"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_features/peer2/exabgp.cfg b/tests/topotests/bgp_features/peer2/exabgp.cfg
new file mode 100644
index 0000000000..1f65547bc5
--- /dev/null
+++ b/tests/topotests/bgp_features/peer2/exabgp.cfg
@@ -0,0 +1,12 @@
+group exabgp {
+ process announce-routes {
+ run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer2.in";
+ encoder text;
+ }
+ neighbor 192.168.101.1 {
+ router-id 192.168.101.4;
+ local-address 192.168.101.4;
+ local-as 65404;
+ peer-as 65000;
+ }
+}
diff --git a/tests/topotests/bgp_features/peer3/exa_readpipe.py b/tests/topotests/bgp_features/peer3/exa_readpipe.py
new file mode 100644
index 0000000000..dba1536388
--- /dev/null
+++ b/tests/topotests/bgp_features/peer3/exa_readpipe.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+"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_features/peer3/exabgp.cfg b/tests/topotests/bgp_features/peer3/exabgp.cfg
new file mode 100644
index 0000000000..8632cc86c5
--- /dev/null
+++ b/tests/topotests/bgp_features/peer3/exabgp.cfg
@@ -0,0 +1,12 @@
+group exabgp {
+ process announce-routes {
+ run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer3.in";
+ encoder text;
+ }
+ neighbor 192.168.101.1 {
+ router-id 192.168.101.5;
+ local-address 192.168.101.5;
+ local-as 65405;
+ peer-as 65000;
+ }
+}
diff --git a/tests/topotests/bgp_features/peer4/exa_readpipe.py b/tests/topotests/bgp_features/peer4/exa_readpipe.py
new file mode 100644
index 0000000000..dba1536388
--- /dev/null
+++ b/tests/topotests/bgp_features/peer4/exa_readpipe.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+"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_features/peer4/exabgp.cfg b/tests/topotests/bgp_features/peer4/exabgp.cfg
new file mode 100644
index 0000000000..06bc0d6e64
--- /dev/null
+++ b/tests/topotests/bgp_features/peer4/exabgp.cfg
@@ -0,0 +1,12 @@
+group exabgp {
+ process announce-routes {
+ run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer4.in";
+ encoder text;
+ }
+ neighbor 192.168.101.1 {
+ router-id 192.168.101.6;
+ local-address 192.168.101.6;
+ local-as 65406;
+ peer-as 65000;
+ }
+}
diff --git a/tests/topotests/bgp_features/r1/bgp_damp_announced.json b/tests/topotests/bgp_features/r1/bgp_damp_announced.json
new file mode 100644
index 0000000000..cb4a2c9b2f
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/bgp_damp_announced.json
@@ -0,0 +1,21 @@
+{
+ "localAS":65000,
+ "routes":{
+ "192.168.31.0/24": [ { "valid":true, "network":"192.168.31.0\/24", "peerId":"192.168.101.3" } ],
+ "192.168.32.0/24": [ { "valid":true, "network":"192.168.32.0\/24", "peerId":"192.168.101.3" } ],
+ "192.168.33.0/24": [ { "valid":true, "network":"192.168.33.0\/24", "peerId":"192.168.101.3" } ],
+ "192.168.34.0/24": [ { "valid":true, "network":"192.168.34.0\/24", "peerId":"192.168.101.3" } ],
+ "192.168.41.0/24": [ { "valid":true, "network":"192.168.41.0\/24", "peerId":"192.168.101.4" } ],
+ "192.168.42.0/24": [ { "valid":true, "network":"192.168.42.0\/24", "peerId":"192.168.101.4" } ],
+ "192.168.43.0/24": [ { "valid":true, "network":"192.168.43.0\/24", "peerId":"192.168.101.4" } ],
+ "192.168.44.0/24": [ { "valid":true, "network":"192.168.44.0\/24", "peerId":"192.168.101.4" } ],
+ "192.168.51.0/24": [ { "valid":true, "network":"192.168.51.0\/24", "peerId":"192.168.101.5" } ],
+ "192.168.52.0/24": [ { "valid":true, "network":"192.168.52.0\/24", "peerId":"192.168.101.5" } ],
+ "192.168.53.0/24": [ { "valid":true, "network":"192.168.53.0\/24", "peerId":"192.168.101.5" } ],
+ "192.168.54.0/24": [ { "valid":true, "network":"192.168.54.0\/24", "peerId":"192.168.101.5" } ],
+ "192.168.61.0/24": [ { "valid":true, "network":"192.168.61.0\/24", "peerId":"192.168.101.6" } ],
+ "192.168.62.0/24": [ { "valid":true, "network":"192.168.62.0\/24", "peerId":"192.168.101.6" } ],
+ "192.168.63.0/24": [ { "valid":true, "network":"192.168.63.0\/24", "peerId":"192.168.101.6" } ],
+ "192.168.64.0/24": [ { "valid":true, "network":"192.168.64.0\/24", "peerId":"192.168.101.6" } ]
+ }
+}
diff --git a/tests/topotests/bgp_features/r1/bgp_damp_setup.json b/tests/topotests/bgp_features/r1/bgp_damp_setup.json
new file mode 100644
index 0000000000..f9f89db894
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/bgp_damp_setup.json
@@ -0,0 +1,10 @@
+{
+ "ipv4Unicast":{
+ "peers":{
+ "192.168.101.3":{"remoteAs":65403, "state":"Established"},
+ "192.168.101.4":{"remoteAs":65404, "state":"Established"},
+ "192.168.101.5":{"remoteAs":65405, "state":"Established"},
+ "192.168.101.6":{"remoteAs":65406, "state":"Established"}
+ }
+ }
+}
diff --git a/tests/topotests/bgp_features/r2/bgp_damp_announced.json b/tests/topotests/bgp_features/r2/bgp_damp_announced.json
new file mode 100644
index 0000000000..9394358f82
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/bgp_damp_announced.json
@@ -0,0 +1,21 @@
+{
+ "localAS":65000,
+ "routes":{
+ "192.168.31.0/24": [ { "network":"192.168.31.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.32.0/24": [ { "network":"192.168.32.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.33.0/24": [ { "network":"192.168.33.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.34.0/24": [ { "network":"192.168.34.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.41.0/24": [ { "network":"192.168.41.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.42.0/24": [ { "network":"192.168.42.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.43.0/24": [ { "network":"192.168.43.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.44.0/24": [ { "network":"192.168.44.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.51.0/24": [ { "network":"192.168.51.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.52.0/24": [ { "network":"192.168.52.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.53.0/24": [ { "network":"192.168.53.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.54.0/24": [ { "network":"192.168.54.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.61.0/24": [ { "network":"192.168.61.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.62.0/24": [ { "network":"192.168.62.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.63.0/24": [ { "network":"192.168.63.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.64.0/24": [ { "network":"192.168.64.0\/24", "peerId":"192.168.0.1" } ]
+ }
+}
diff --git a/tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json b/tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json
new file mode 100644
index 0000000000..f3c54a70a1
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json
@@ -0,0 +1,18 @@
+{
+ "192.168.31.0/24": null,
+ "192.168.32.0/24": null,
+ "192.168.33.0/24": null,
+ "192.168.34.0/24": null,
+ "192.168.41.0/24": null,
+ "192.168.42.0/24": null,
+ "192.168.43.0/24": null,
+ "192.168.44.0/24": null,
+ "192.168.51.0/24": null,
+ "192.168.52.0/24": null,
+ "192.168.53.0/24": null,
+ "192.168.54.0/24": null,
+ "192.168.61.0/24": null,
+ "192.168.62.0/24": null,
+ "192.168.63.0/24": null,
+ "192.168.64.0/24": null
+}
diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py
index bc821bd7a0..3d963b4cf6 100644
--- a/tests/topotests/bgp_features/test_bgp_features.py
+++ b/tests/topotests/bgp_features/test_bgp_features.py
@@ -33,6 +33,7 @@ import sys
import pytest
import re
import time
+from time import sleep
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
@@ -64,6 +65,14 @@ class BGPFeaturesTopo1(Topo):
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))
@@ -89,6 +98,12 @@ class BGPFeaturesTopo1(Topo):
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"])
+
#####################################################
#
@@ -753,41 +768,71 @@ def test_bgp_delayopen_without():
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")
+ 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"')
+ 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))
+ 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))
+ 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)
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
_, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
assertmsg = "BGP session on r{} did not shut down peer".format(router_num)
assert res is None, assertmsg
# 1.3 disable peering shutdown
logger.info("Disable shutdown of peering between r1 and r4")
- tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 shutdown"')
- tgen.net["r4"].cmd('vtysh -c "conf t" -c "router bgp 65100" -c "no neighbor 192.168.101.1 shutdown"')
+ 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))
+ 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))
+ 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)
+ 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)
+ assertmsg = (
+ "BGP session on r{} did not establish a connection with peer".format(
+ router_num
+ )
+ )
assert res is None, assertmsg
- #tgen.mininet_cli()
+ # tgen.mininet_cli()
# end test_bgp_delayopen_without
@@ -800,65 +845,107 @@ def test_bgp_delayopen_singular():
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")
+ 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"')
+ 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))
+ 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))
+ 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)
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
_, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
assertmsg = "BGP session on r{} did not shut down peer".format(router_num)
assert res is None, assertmsg
# 2.3 set delayopen on R1 to 240
logger.info("Setting DelayOpenTime for neighbor r4 to 240 seconds on r1")
- tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 timers delayopen 240"')
+ 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)
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show bgp neighbors json", expected
+ )
_, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
assertmsg = "BGP session on r1 failed to set DelayOpenTime for r4"
assert res is None, assertmsg
# 2.5 disable peering shutdown
logger.info("Disable shutdown of peering between r1 and r4")
- tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 shutdown"')
- tgen.net["r4"].cmd('vtysh -c "conf t" -c "router bgp 65100" -c "no neighbor 192.168.101.1 shutdown"')
+ 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))
+ 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))
+ 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)
+ 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)
+ 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"')
+ 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()
+ 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()
+ # tgen.mininet_cli()
# end test_bgp_delayopen_singular
@@ -870,67 +957,119 @@ def test_bgp_delayopen_dual():
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")
+ 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"')
+ 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))
+ 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))
+ 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)
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
_, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
assertmsg = "BGP session on r{} did not shut down peer".format(router_num)
assert res is None, assertmsg
# 3.3 set delayopen on R2 to 60s and on R5 to 30s
logger.info("Setting DelayOpenTime for neighbor r5 to 60 seconds on r2")
- tgen.net["r2"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.201.2 timers delayopen 60"')
+ 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"')
+ 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))
+ 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))
+ 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)
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show bgp neighbors json", expected
+ )
_, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
assertmsg = "BGP session on r{} failed to set DelayOpenTime".format(router_num)
assert res is None, assertmsg
## 3.5 disable peering shutdown
logger.info("Disable shutdown of peering between r2 and r5")
- tgen.net["r2"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.201.2 shutdown"')
- tgen.net["r5"].cmd('vtysh -c "conf t" -c "router bgp 65200" -c "no neighbor 192.168.201.1 shutdown"')
+ 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))
+ 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))
+ 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)
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
_, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
- assertmsg = "BGP session on r{} did not enter Connect state with peer".format(router_num)
+ 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))
+ 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))
+ 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)
+ 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)
+ 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())
@@ -939,22 +1078,692 @@ def test_bgp_delayopen_dual():
# 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"')
+ 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"')
+ 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)
+ 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()
+ # tgen.mininet_cli()
# end test_bgp_delayopen_dual
+def test_bgp_dampening_setup():
+ "BGP route-flap dampening test setup"
+
+ # This test starts four ExaBGP peers, adds them as neighbors to the
+ # configuration of router r1 and checks if connections get established.
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting BGP route-flap dampening test setup")
+
+ # Start ExaBGP peers connected to r1 via switch 4
+ logger.info("Starting ExaBGP peers")
+ for peer_num in range(1, 5):
+ logger.info("Creating named pipe for ExaBGP peer peer{}".format(peer_num))
+ fifo_in = "/var/run/exabgp_peer{}.in".format(peer_num)
+ if os.path.exists(fifo_in):
+ os.remove(fifo_in)
+ os.mkfifo(fifo_in, 0o777)
+ logger.info("Starting ExaBGP on peer peer{}".format(peer_num))
+ peer = tgen.gears["peer{}".format(peer_num)]
+ peer_dir = os.path.join(CWD, "peer{}".format(peer_num))
+ env_file = os.path.join(CWD, "exabgp.env")
+ peer.start(peer_dir, env_file)
+
+ # Add ExaBGP peers to configuration of router r2
+ logger.info("Adding ExaBGP peers as neighbors to configuration of router r2")
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.3 remote-as 65403"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 route-map testmap-in"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 route-map testmap-out"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.4 remote-as 65404"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.4 route-map testmap-in"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.4 route-map testmap-out"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.5 remote-as 65405"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 route-map testmap-in"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 route-map testmap-out"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.6 remote-as 65406"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.6 route-map testmap-in"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.6 route-map testmap-out"'
+ )
+
+ # Check if exabgp peers are up and running
+ logger.info("Checking for established connections to ExaBGP peers on router r1")
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/bgp_damp_setup.json")
+ 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=10, wait=1)
+ assertmsg = (
+ "BGP session on r1 did not establish connections with one ore more ExaBGP peers"
+ )
+ assert res is None, assertmsg
+
+ # end test_bgp_dampening_setup
+
+
+def test_bgp_dampening_route_announce():
+ "Test of BGP route-flap dampening route announcement"
+
+ # This test checks if the four ExaBGP peers can announce routes to router
+ # r1 and if these routes get forwarded to router r2.
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting test of BGP route-flap dampening route announcement")
+
+ # Announce routes on exabgp peers to r2
+ logger.info("Announcing routes on ExaBGP peers to r1")
+ for prefix_iter in range(1, 5):
+ for peer_num in range(1, 5):
+ pipe = open("/run/exabgp_peer{}.in".format(peer_num), "w")
+ with pipe:
+ pipe.write(
+ "announce route 192.168.{}{}.0/24 next-hop 192.168.101.{}\n".format(
+ (peer_num + 2), prefix_iter, (peer_num + 2)
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+
+ # Check if routes announced by ExaBGP peers are present in RIB of router r1
+ logger.info(
+ "Checking if routes announced by ExaBGP peers are present in RIB of router r1"
+ )
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/bgp_damp_announced.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 = (
+ "BGP session on router r1 did not receive routes announced by ExaBGP peers"
+ )
+ assert res is None, assertmsg
+
+ # Check if routes announced by ExaBGP peers to router r1 have been forwarded
+ # and are now present in RIB of router r2
+ logger.info(
+ "Checking if forwarded routes announced by ExaBGP peers are present in RIB of router r2"
+ )
+ router = tgen.gears["r2"]
+ reffile = os.path.join(CWD, "r2/bgp_damp_announced.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 = "BGP session on router r2 did not receive routes announced by ExaBGP peers forwarded by router r1"
+ assert res is None, assertmsg
+
+ # end test_bgp_dampening_route_announce
+
+
+def test_bgp_dampening_disabled():
+ "Test of BGP route-flapping with dampening disabled"
+
+ # This test verifies that flapped routes do not get withdrawn from the RIB
+ # of router r1 if dampening is disabled.
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting test of BGP route-flapping with dampening disabled")
+
+ # Flapping routes on ExaBGP peer peer1
+ logger.info(
+ "Flapping routes on ExaBGP peer peer1 with route-flap dampening disabled"
+ )
+ for _ in range(1, 5):
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer1.in", "w")
+ with pipe:
+ pipe.write(
+ "withdraw route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+ sleep(1) # Give the BGP session on router r1 time to process routes
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer1.in", "w")
+ with pipe:
+ pipe.write(
+ "announce route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+
+ # Verify flapped routes are still present in RIB of router r1
+ logger.info(
+ "Verifying that the flapped routes are still present in RIB of router r1"
+ )
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/bgp_damp_announced.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 = "BGP session on router r1 removed flapped routes despite route-flap dampening being disabled"
+ assert res is None, assertmsg
+
+ # end test_bgp_dampening_disabled
+
+
+def test_bgp_dampening_config():
+ "Test of BGP route-flap dampening configuration"
+
+ # This test adds peer-group group1 with peers peer1 and peer2 to the
+ # configuration of router r1, sets up dampening configurations with
+ # different profiles and verifies the configured dampening parameters.
+
+ tgen = get_topogen()
+ r_1 = tgen.net["r1"]
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting test of BGP route-flap dampening configuration")
+
+ # Add peer-group group1 with peers peer1 and peer2
+ logger.info(
+ "Creating peer-group group1 and adding ExaBGP peers peer1 and peer2 to it"
+ )
+ r_1.cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor group1 peer-group"')
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.3 peer-group group1"'
+ ) # peer1
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.4 peer-group group1"'
+ ) # peer2
+
+ # Enable different dampening profiles for peer1, peer3, group1 and global
+ # configuration
+ logger.info(
+ "Enabling different dampening profiles for peer1, peer3, group1 and global configuration"
+ )
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "bgp dampening 30 300 900 90"'
+ )
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor group1 dampening 20 200 600 60"'
+ )
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 dampening 10 100 300 30"'
+ ) # peer1
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 dampening 10 100 300 30"'
+ ) # peer3
+
+ # Verify route-flap dampening configuration
+ logger.info("Verifying route-flap dampening configuration on router r1")
+ vtyout = r_1.cmd('vtysh -c "show running-config"')
+ assertmsg = "BGP Session on r1 does not show enabled global route-flap dampening in running configuration"
+ assert re.search("bgp dampening 30 300 900 90", vtyout), assertmsg
+ assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer-group group1 in running configuration"
+ assert re.search("neighbor group1 dampening 20 200 600 60", vtyout), assertmsg
+ assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer peer1 in running configuration"
+ assert re.search(
+ "neighbor 192.168.101.3 dampening 10 100 300 30", vtyout
+ ), assertmsg
+ assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer peer3 in running configuration"
+ assert re.search(
+ "neighbor 192.168.101.5 dampening 10 100 300 30", vtyout
+ ), assertmsg
+
+ # end test_bgp_dampening_config
+
+
+def test_bgp_dampening_profile_peer_over_group():
+ "Test of BGP route-flap dampening profile preferences: peer over group"
+
+ # This test verifies that the dampening profile of a peer takes precedence
+ # over the dampening profile of its peer-group by flapping the peers routes
+ # until dampened and comparing the reuse times to the one specified in the
+ # dampening configuration.
+
+ tgen = get_topogen()
+ r_1 = tgen.net["r1"]
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Starting test of BGP route-flap dampening profile preferences: peer over group"
+ )
+
+ # Flapping routes on ExaBGP peer peer1
+ logger.info(
+ "Flapping routes on ExaBGP peer peer1 with route-flap dampening enabled"
+ )
+ for _ in range(1, 5):
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer1.in", "w")
+ with pipe:
+ pipe.write(
+ "withdraw route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+ sleep(1) # Give the BGP session on router r1 time to process routes
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer1.in", "w")
+ with pipe:
+ pipe.write(
+ "announce route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+
+ # Check damped paths on r1 for routes of peer1 witn peer profile
+ logger.info(
+ "Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer1"
+ )
+ sleep(5) # Wait 5 seconds for paths to show up in dampened-paths list
+ vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"')
+ routes = re.findall(r"\*d 192\.168\.3\d\.0\/24.*", vtyout)
+ assertmsg = (
+ "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer1"
+ )
+ assert len(routes) == 4, assertmsg
+ assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer1"
+ for route in routes:
+ assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time
+ 35 > int(route.split()[3].split(":")[1]) > 25
+ ), assertmsg # minutes of reuse time
+
+ # end test_bgp_dampening_profile_peer_over_group
+
+
+def test_bgp_dampening_profile_group_over_global():
+ "Test of BGP route-flap dampening profile preferences: group over global"
+
+ # This test verifies that the dampening profile of a peer-group takes
+ # precedence over the global dampening profile by flapping the routes of a
+ # peer-group member until dampened and comparing the reuse times to the one
+ # specified in the dampening configuration.
+
+ tgen = get_topogen()
+ r_1 = tgen.net["r1"]
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Starting test of BGP route-flap dampening profile preferences: group over global"
+ )
+
+ # Flapping routes on ExaBGP peer peer2
+ logger.info(
+ "Flapping routes on ExaBGP peer peer2 with route-flap dampening enabled"
+ )
+ for _ in range(1, 5):
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer2.in", "w")
+ with pipe:
+ pipe.write(
+ "withdraw route 192.168.4{}.0/24 next-hop 192.168.101.4\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+ sleep(1) # Give the BGP session on router r1 time to process routes
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer2.in", "w")
+ with pipe:
+ pipe.write(
+ "announce route 192.168.4{}.0/24 next-hop 192.168.101.4\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+
+ # Check damped paths on r1 for routes of peer2 witn group profile
+ logger.info(
+ "Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer2"
+ )
+ sleep(5) # wait 5 seconds for paths to shop up in damp list
+ vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"')
+ routes = re.findall(r"\*d 192\.168\.4\d\.0\/24.*", vtyout)
+ assertmsg = (
+ "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer2"
+ )
+ assert len(routes) == 4, assertmsg
+ assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer2"
+ for route in routes:
+ assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time
+ 65 > int(route.split()[3].split(":")[1]) > 55
+ ), assertmsg # minutes of reuse time
+
+ # end test_bgp_dampening_profile_group_over_global
+
+
+def test_bgp_dampening_profile_peer_over_global():
+ "Test of BGP route-flap dampening profile preferences: peer over global"
+
+ # This test verifies that the dampening profile of a peer takes precedence
+ # over the global dampening profile by flapping the routes of the peer until
+ # dampened and comparing the reuse times to the one specified in the
+ # dampening configuration.
+
+ tgen = get_topogen()
+ r_1 = tgen.net["r1"]
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Starting test of BGP route-flap dampening profile preferences: peer over global"
+ )
+
+ # Flapping routes on ExaBGP peer peer3
+ logger.info(
+ "Flapping routes on ExaBGP peer peer3 with route-flap dampening enabled"
+ )
+ for _ in range(1, 5):
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer3.in", "w")
+ with pipe:
+ pipe.write(
+ "withdraw route 192.168.5{}.0/24 next-hop 192.168.101.5\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+ sleep(1) # Give the BGP session on router r1 time to process routes
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer3.in", "w")
+ with pipe:
+ pipe.write(
+ "announce route 192.168.5{}.0/24 next-hop 192.168.101.5\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+
+ # Check damped paths on r1 for routes of peer3 witn peer profile
+ logger.info(
+ "Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer3"
+ )
+ sleep(5) # wait 5 seconds for paths to shop up in damp list
+ vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"')
+ routes = re.findall(r"\*d 192\.168\.5\d\.0\/24.*", vtyout)
+ assertmsg = (
+ "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer3"
+ )
+ assert len(routes) == 4, assertmsg
+ assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer3"
+ for route in routes:
+ assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time
+ 35 > int(route.split()[3].split(":")[1]) > 25
+ ), assertmsg # minutes of reuse time
+
+ # end test_bgp_dampening_profile_peer_over_global
+
+
+def test_bgp_dampening_profile_global():
+ "Test of BGP route-flap dampening global profile"
+
+ # This test verifies the application of the global dampening profile by
+ # flapping the routes of a peer until dampened and comparing the reuse times
+ # to the one specified in the dampening configuration.
+
+ tgen = get_topogen()
+ r_1 = tgen.net["r1"]
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting test of BGP route-flap dampening global profile")
+
+ # Flapping routes on ExaBGP peer peer4
+ logger.info(
+ "Flapping routes on ExaBGP peer peer4 with route-flap dampening enabled"
+ )
+ for _ in range(1, 5):
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer4.in", "w")
+ with pipe:
+ pipe.write(
+ "withdraw route 192.168.6{}.0/24 next-hop 192.168.101.6\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+ sleep(1) # Give the BGP session on router r1 time to process routes
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer4.in", "w")
+ with pipe:
+ pipe.write(
+ "announce route 192.168.6{}.0/24 next-hop 192.168.101.6\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+
+ # Check damped paths on r1 for routes of peer4 witn global profile
+ logger.info(
+ "Checking if router r1 used the global dampening profile on routes flapped by ExaBGP peer peer4"
+ )
+ sleep(5) # wait 5 seconds for paths to shop up in damp list
+ vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"')
+ routes = re.findall(r"\*d 192\.168\.6\d\.0\/24.*", vtyout)
+ assertmsg = (
+ "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer4"
+ )
+ assert len(routes) == 4, assertmsg
+ assertmsg = "BGP session on router r1 did not use the global dampening profile for a route flapped by ExaBGP peer peer4"
+ for route in routes:
+ assert (int(route.split()[3].split(":")[0]) == 1) and ( # hours of reuse time
+ 35 > int(route.split()[3].split(":")[1]) > 25
+ ), assertmsg # minutes of reuse time
+
+ # end test_bgp_dampening_profile_global
+
+
+def test_bgp_dampening_withdaw():
+ "Test BGP route-flap dampening route withdraw"
+
+ # This test verifies that the withrawl of dampened routes from the RIB of
+ # router r1 was propagated to router r2.
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting test of BGP route-flap dampening route withdraw")
+
+ # Check if routes dampened on router r1 have been withdrawn from the RIB on
+ # router r2
+ logger.info(
+ "Checking if routes dampened on router r1 have been withdrawn of RIB on router r2"
+ )
+ reffile = os.path.join(CWD, "r2/bgp_damp_withdrawn.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, tgen.gears["r2"], "show ip bgp json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=1)
+ assertmsg = "BGP session on router r2 did not receive withdraw of routes dampened on router r1"
+ assert res is None, assertmsg
+
+ # end test_bgp_dampening_withdaw
+
+
+def test_bgp_dampening_cleanup():
+ "BGP route-flap dampening test cleanup"
+
+ # This test cleans up after other tests associated with route-flap dampening
+ # by disabling all dampening configurations, removing added peers and
+ # peer-groups from the configuration on router r1, and shutting down ExaBGP
+ # peers peer1, peer2 and peer3.
+
+ tgen = get_topogen()
+ r_1 = tgen.net["r1"]
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting BGP route-flap dampening test cleanup")
+
+ # Disable all dampening configurations
+ logger.info("Disabling all dampening configurations")
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no bgp dampening"'
+ )
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor group1 dampening"'
+ )
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor 192.168.101.3 dampening"'
+ )
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor 192.168.101.5 dampening"'
+ )
+
+ # Remove ExaBGP peers from configuration of router r1
+ logger.info("Removing ExaBGP peers from configuration of router r1")
+ for router_num in range(3, 7):
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.{}"'.format(
+ router_num
+ )
+ )
+
+ # Remove peer-group group1 from configuration of router r1
+ logger.info("Removing peer-group group1 peers from configuration of router r1")
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor group1 peer-group"'
+ )
+
+ # Stop ExaBGP peers and remove associated named pipes
+ logger.info("Stopping ExaBGP peers and removing associated named pipes")
+ for peer_num in range(1, 5):
+ logger.info("Terminating ExaBGP on peer peer{}".format(peer_num))
+ peer = tgen.gears["peer{}".format(peer_num)]
+ logger.info("Removing named pipe of ExaBGP peer peer{}".format(peer_num))
+ fifo_in = "/var/run/exabgp_peer{}.in".format(peer_num)
+ peer.stop()
+ if os.path.exists(fifo_in):
+ os.remove(fifo_in)
+
+ # end test_bgp_dampening_cleanup
+
+
+def test_bgp_dampening_aftermath():
+ "BGP route-flap dampening aftermath test"
+
+ # This test verifies routers r1 and r2 not being affected by the route-flap
+ # dampening test series.
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Check BGP Summary on routers r1 and r2
+ for rtr_num in [1, 2]:
+ logger.info(
+ "Checking if BGP router on r{} remains unaffected by route-flap dampening tests".format(
+ rtr_num
+ )
+ )
+ router = tgen.gears["r{}".format(rtr_num)]
+ reffile = os.path.join(CWD, "r{}/show_bgp.json".format(rtr_num))
+ 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=2)
+ assertmsg = "BGP routes on router r{} are wrong after route-flap dampening tests".format(
+ rtr_num
+ )
+ assert res is None, assertmsg
+
+ # end test_bgp_dampening_aftermath
+
+
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.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py
index 097b654e77..3a2d283025 100644
--- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py
+++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py
@@ -316,7 +316,9 @@ def test_BGP_GR_TC_46_p1(request):
input_dict = {
"r1": {
"bgp": {
- "graceful-restart": {"graceful-restart": True,},
+ "graceful-restart": {
+ "graceful-restart": True,
+ },
"address_family": {
"ipv4": {
"unicast": {
@@ -1184,8 +1186,8 @@ def test_BGP_GR_TC_53_p1(request):
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.
+ 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()
@@ -1368,9 +1370,9 @@ def test_BGP_GR_TC_4_p0(request):
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.
+ 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()
@@ -1486,7 +1488,7 @@ def test_BGP_GR_TC_5_1_2_p1(request):
shutdown_bringup_interface(tgen, "r2", intf)
# Bring up Interface
- shutdown_bringup_interface(tgen, dut, intf, ifaceaction=True)
+ shutdown_bringup_interface(tgen, "r2", intf, ifaceaction=True)
for addr_type in ADDR_TYPES:
result = verify_graceful_restart(
@@ -1773,7 +1775,7 @@ def test_BGP_GR_TC_6_1_2_p1(request):
shutdown_bringup_interface(tgen, "r2", intf)
# Bring up Interface
- shutdown_bringup_interface(tgen, dut, intf, ifaceaction=True)
+ shutdown_bringup_interface(tgen, "r2", intf, ifaceaction=True)
for addr_type in ADDR_TYPES:
result = verify_graceful_restart(
@@ -1815,8 +1817,8 @@ def test_BGP_GR_TC_6_1_2_p1(request):
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.
+ 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()
@@ -1959,8 +1961,8 @@ def test_BGP_GR_TC_8_p1(request):
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.
+ Test Objective : Verify that only GR helper routers keep the stale
+ route entries, not any GR disabled router.
"""
tgen = get_topogen()
@@ -2145,8 +2147,8 @@ def test_BGP_GR_TC_17_p1(request):
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.
+ 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()
@@ -2325,8 +2327,8 @@ def test_BGP_GR_TC_19_p1(request):
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.
+ 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
@@ -3090,8 +3092,8 @@ def test_BGP_GR_TC_31_2_p1(request):
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.
+ 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()
@@ -3264,8 +3266,8 @@ def test_BGP_GR_TC_9_p1(request):
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.
+ Test Objective : Verify that only GR helper routers keep the stale
+ route entries, not any GR disabled router.
"""
tgen = get_topogen()
@@ -3467,7 +3469,13 @@ def test_BGP_GR_TC_43_p1(request):
step("Configure R1 and R2 as GR restarting node in global level")
input_dict = {
- "r1": {"bgp": {"graceful-restart": {"graceful-restart": True,}}},
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ },
"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
}
@@ -3560,7 +3568,13 @@ def test_BGP_GR_TC_43_p1(request):
step("Verify on R2 that R1 doesn't advertise any GR capabilities")
input_dict = {
- "r1": {"bgp": {"graceful-restart": {"graceful-restart-disable": True,}}},
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-disable": True,
+ }
+ }
+ },
"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
}
@@ -3659,7 +3673,13 @@ def test_BGP_GR_TC_43_p1(request):
step("Verify on R2 that R1 advertises GR capabilities as a restarting node")
input_dict = {
- "r1": {"bgp": {"graceful-restart": {"graceful-restart": True,}}},
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ },
"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
}
@@ -3779,7 +3799,13 @@ def test_BGP_GR_TC_44_p1(request):
step("Verify on R2 that R1 advertises GR capabilities as a helper node")
input_dict = {
- "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True,}}},
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-helper": True,
+ }
+ }
+ },
"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
}
@@ -3849,7 +3875,13 @@ def test_BGP_GR_TC_44_p1(request):
start_router_daemons(tgen, "r2", ["bgpd"])
input_dict = {
- "r1": {"bgp": {"graceful-restart": {"graceful-restart-disable": True,}}}
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-disable": True,
+ }
+ }
+ }
}
configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
@@ -3857,7 +3889,13 @@ def test_BGP_GR_TC_44_p1(request):
step("Verify on R2 that R1 doesn't advertise any GR capabilities")
input_dict = {
- "r1": {"bgp": {"graceful-restart": {"graceful-restart-disable": True,}}},
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-disable": True,
+ }
+ }
+ },
"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
}
@@ -3941,7 +3979,13 @@ def test_BGP_GR_TC_44_p1(request):
step("Verify on R2 that R1 advertises GR capabilities as a helper node")
input_dict = {
- "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True,}}},
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-helper": True,
+ }
+ }
+ },
"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
}
@@ -4108,14 +4152,28 @@ def test_BGP_GR_TC_45_p1(request):
start_router_daemons(tgen, "r1", ["bgpd"])
- input_dict = {"r1": {"bgp": {"graceful-restart": {"graceful-restart": False,}}}}
+ 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,}}},
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-helper": True,
+ }
+ }
+ },
"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
}
@@ -4199,14 +4257,28 @@ def test_BGP_GR_TC_45_p1(request):
start_router_daemons(tgen, "r2", ["bgpd"])
- input_dict = {"r1": {"bgp": {"graceful-restart": {"graceful-restart": True,}}}}
+ 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,}}},
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ },
"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
}
@@ -4307,7 +4379,9 @@ def test_BGP_GR_TC_46_p1(request):
input_dict = {
"r1": {
"bgp": {
- "graceful-restart": {"graceful-restart": True,},
+ "graceful-restart": {
+ "graceful-restart": True,
+ },
"address_family": {
"ipv4": {
"unicast": {
@@ -4559,7 +4633,9 @@ def test_BGP_GR_TC_47_p1(request):
input_dict = {
"r1": {
"bgp": {
- "graceful-restart": {"graceful-restart": True,},
+ "graceful-restart": {
+ "graceful-restart": True,
+ },
"address_family": {
"ipv4": {
"unicast": {
@@ -4698,7 +4774,13 @@ def test_BGP_GR_TC_47_p1(request):
step("Verify on R2 that R1 still advertises GR capabilities as a restarting node")
input_dict = {
- "r1": {"bgp": {"graceful-restart": {"graceful-restart": True,}}},
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ },
"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
}
@@ -4814,7 +4896,9 @@ def test_BGP_GR_TC_48_p1(request):
input_dict = {
"r1": {
"bgp": {
- "graceful-restart": {"graceful-restart": True,},
+ "graceful-restart": {
+ "graceful-restart": True,
+ },
"address_family": {
"ipv4": {
"unicast": {
@@ -4960,7 +5044,13 @@ def test_BGP_GR_TC_48_p1(request):
step("Verify on R2 that R1 advertises GR capabilities as a restarting node")
input_dict = {
- "r1": {"bgp": {"graceful-restart": {"graceful-restart": True,}}},
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ },
"r2": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}},
}
diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py
index 6926121a6b..2ddeab13f6 100644
--- a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py
+++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py
@@ -456,7 +456,9 @@ def test_BGP_GR_TC_3_p0(request):
input_dict = {
"r1": {
"bgp": {
- "graceful-restart": {"disable-eor": True,},
+ "graceful-restart": {
+ "disable-eor": True,
+ },
"address_family": {
"ipv4": {
"unicast": {
@@ -2095,7 +2097,10 @@ def test_BGP_GR_chaos_33_p1(request):
"ipv4": {
"unicast": {
"advertise_networks": [
- {"network": "200.0.20.1/32", "no_of_network": 2,}
+ {
+ "network": "200.0.20.1/32",
+ "no_of_network": 2,
+ }
]
}
},
@@ -2207,13 +2212,13 @@ def test_BGP_GR_chaos_33_p1(request):
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 Error {}". \
- format(tc_name, result)
- logger.info(" Expected behavior: {}".\
- format(result))
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_2, next_hop_6, expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n Error {}".format(
+ tc_name, result
+ )
+ logger.info(" Expected behavior: {}".format(result))
logger.info("[Step 4] : Start BGPd daemon on R1 and R4..")
@@ -3960,7 +3965,13 @@ def test_BGP_GR_21_p2(request):
}
}
},
- "r2": {"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")
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
index 40489f438f..31fbdcd4b5 100644
--- 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
@@ -1174,6 +1174,39 @@ def test_large_community_boundary_values(request):
)
+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
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
index 9c0355a3e9..c2858a4bd0 100644
--- 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
@@ -2132,7 +2132,11 @@ def test_large_community_lists_with_rmap_match_regex(request):
{
"action": "permit",
"seq_id": "10",
- "match": {"large_community_list": {"id": "ALL",},},
+ "match": {
+ "large_community_list": {
+ "id": "ALL",
+ },
+ },
}
]
}
@@ -2208,7 +2212,11 @@ def test_large_community_lists_with_rmap_match_regex(request):
{
"action": "permit",
"seq_id": "20",
- "match": {"large_community_list": {"id": "EXP_ALL",},},
+ "match": {
+ "large_community_list": {
+ "id": "EXP_ALL",
+ },
+ },
}
]
}
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 0000000000..95de8cc10c
--- /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 0000000000..d773e87ef6
--- /dev/null
+++ b/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_listen_on_multiple_addresses.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by Boeing Defence Australia
+# Adriano Marto Reis
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_listen_on_multiple_addresses.py: Test BGP daemon listening for
+connections on multiple addresses.
+
+ +------+ +------+ +------+ +------+
+ | | | | | | | |
+ | r1 |--------| r2 |--------| r3 |--------| r4 |
+ | | | | | | | |
+ +------+ +------+ +------+ +------+
+
+ | | | |
+ | AS 1000 | AS 2000 | AS 3000 |
+ | | | |
+ +------------+--------------------------------+-------------+
+"""
+
+import os
+import sys
+import 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, "../"))
+
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_topo_from_json, build_config_from_json
+from lib.common_config import start_topology
+from lib.topotest import router_json_cmp, run_and_expect
+from mininet.topo import Topo
+from functools import partial
+
+
+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"],
+}
+
+
+# Reads data from JSON File for topology and configuration creation.
+jsonFile = "{}/bgp_listen_on_multiple_addresses.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+
+class TemplateTopo(Topo):
+ "Topology builder."
+
+ def build(self, *_args, **_opts):
+ "Defines the allocation and relationship between routers and switches."
+ tgen = get_topogen(self)
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ "Sets up the test environment."
+ tgen = Topogen(TemplateTopo, mod.__name__)
+
+ # 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)
+ 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_lu_topo1/R1/bgpd.conf b/tests/topotests/bgp_lu_topo1/R1/bgpd.conf
new file mode 100644
index 0000000000..1bdb4c7a3e
--- /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 0000000000..29e6c2cbf7
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json
@@ -0,0 +1,8 @@
+{
+ "Ledger":506,
+ "InUse":506,
+ "Requests":0,
+ "LabelChunks":11,
+ "Pending":0,
+ "Reconnects":0
+}
diff --git a/tests/topotests/bgp_lu_topo1/R1/zebra.conf b/tests/topotests/bgp_lu_topo1/R1/zebra.conf
new file mode 100644
index 0000000000..4f6fee579f
--- /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 0000000000..bac608e1c3
--- /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 0000000000..29e6c2cbf7
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json
@@ -0,0 +1,8 @@
+{
+ "Ledger":506,
+ "InUse":506,
+ "Requests":0,
+ "LabelChunks":11,
+ "Pending":0,
+ "Reconnects":0
+}
diff --git a/tests/topotests/bgp_lu_topo1/R2/zebra.conf b/tests/topotests/bgp_lu_topo1/R2/zebra.conf
new file mode 100644
index 0000000000..33ee53efe7
--- /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 0000000000..b42df022e0
--- /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 0000000000..524978bff6
--- /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 0000000000..61418d7a79
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/test_bgp_lu.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_lu.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_lu.py: Test BGP LU label allocation
+"""
+
+import os
+import sys
+import json
+from functools import partial
+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.
+from mininet.topo import Topo
+
+#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 | |
+#+-----+ +-----+ +-----+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ # 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(TemplateTopo, 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 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_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_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py
index 9703cf8d57..19a9140c13 100644
--- 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
@@ -95,7 +95,7 @@ from lib.common_config import (
kill_router_daemons,
start_router_daemons,
stop_router,
- start_router
+ start_router,
)
from lib.topolog import logger
@@ -129,7 +129,7 @@ LOOPBACK_2 = {
"ipv4": "20.20.20.20/32",
"ipv6": "20::20:20/128",
"ipv4_mask": "255.255.255.255",
- "ipv6_mask": None
+ "ipv6_mask": None,
}
MAX_PATHS = 2
@@ -724,16 +724,40 @@ def test_vrf_with_multiple_links_p1(request):
"local_as": "200",
"vrf": "RED_A",
"address_family": {
- "ipv4": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}},
- "ipv6": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}},
+ "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,}}},
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": MAX_PATHS,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": MAX_PATHS,
+ }
+ }
+ },
},
},
]
@@ -2148,7 +2172,7 @@ def test_restart_bgpd_daemon_p1(request):
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)
+ assert result is True, "Testcase () :Failed\n Error {}".format(tc_name, result)
step("Kill BGPd daemon on R1.")
kill_router_daemons(tgen, "r1", ["bgpd"])
@@ -2663,6 +2687,9 @@ def test_delete_and_re_add_vrf_p1(request):
}
}
+ 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)
diff --git a/tests/topotests/bgp_peer-group/__init__.py b/tests/topotests/bgp_peer-group/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /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 0000000000..19b490a359
--- /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 0000000000..e2c399e536
--- /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 0000000000..0880ee9fae
--- /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 0000000000..606c17bec9
--- /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 0000000000..eb2fca15fb
--- /dev/null
+++ b/tests/topotests/bgp_peer-group/r3/bgpd.conf
@@ -0,0 +1,7 @@
+!
+router bgp 65003
+ neighbor PG peer-group
+ neighbor PG remote-as external
+ neighbor PG timers 3 10
+ neighbor 192.168.255.1 peer-group PG
+!
diff --git a/tests/topotests/bgp_peer-group/r3/zebra.conf b/tests/topotests/bgp_peer-group/r3/zebra.conf
new file mode 100644
index 0000000000..e9fdfb70c5
--- /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 0000000000..7c7a8b87ed
--- /dev/null
+++ b/tests/topotests/bgp_peer-group/test_bgp_peer-group.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Test if peer-group works for numbered and unnumbered configurations.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from mininet.topo import Topo
+
+
+class TemplateTopo(Topo):
+ def build(self, *_args, **_opts):
+ tgen = get_topogen(self)
+
+ 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(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_peer_group():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_peer_group_configured():
+ output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor json"))
+ expected = {
+ "r1-eth0": {"peerGroup": "PG", "bgpState": "Established"},
+ "192.168.255.3": {"peerGroup": "PG", "bgpState": "Established"},
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_peer_group_configured)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r1"])
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py
index 6d7131e1e5..ceac84709b 100644
--- a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py
+++ b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py
@@ -101,7 +101,11 @@ def test_r1_receive_and_advertise_prefix_sid_type1():
"prefix": prefix,
"advertisedTo": {"10.0.0.101": {}, "10.0.0.102": {}},
"paths": [
- {"valid": True, "remoteLabel": remoteLabel, "labelIndex": labelIndex,}
+ {
+ "valid": True,
+ "remoteLabel": remoteLabel,
+ "labelIndex": labelIndex,
+ }
],
}
return topotest.json_cmp(output, expected)
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
index 3af944473d..3f9009967d 100644
--- 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
@@ -1042,9 +1042,10 @@ def test_next_hop_with_recursive_lookup_p1(request):
next_hop=next_hop,
expected=False,
)
- assert result is not True, (
- "Testcase {} : Failed \n "
- "Route is still present \n Error : {}".format(tc_name, result)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Route is still present \n Error : {}".format(
+ tc_name, result
)
step("Re-apply redistribution on R4.")
@@ -1125,9 +1126,10 @@ def test_next_hop_with_recursive_lookup_p1(request):
next_hop=next_hop,
expected=False,
)
- assert result is not True, (
- "Testcase {} : Failed \n "
- "Route is still present \n Error : {}".format(tc_name, result)
+ 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)
@@ -1182,9 +1184,10 @@ def test_next_hop_with_recursive_lookup_p1(request):
next_hop=next_hop,
expected=False,
)
- assert result is not True, (
- "Testcase {} : Failed \n "
- "Route is still present \n Error : {}".format(tc_name, result)
+ 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)
@@ -2101,8 +2104,20 @@ def test_BGP_active_standby_preemption_and_ecmp_p1(request):
"r4": {
"bgp": {
"address_family": {
- "ipv4": {"unicast": {"maximum_paths": {"ebgp": 1,}}},
- "ipv6": {"unicast": {"maximum_paths": {"ebgp": 1,}}},
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": 1,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": 1,
+ }
+ }
+ },
}
}
}
@@ -2131,8 +2146,20 @@ def test_BGP_active_standby_preemption_and_ecmp_p1(request):
"r4": {
"bgp": {
"address_family": {
- "ipv4": {"unicast": {"maximum_paths": {"ebgp": 2,}}},
- "ipv6": {"unicast": {"maximum_paths": {"ebgp": 2,}}},
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": 2,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": 2,
+ }
+ }
+ },
}
}
}
diff --git a/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py
index 0fabd90341..0467bf1bfb 100644
--- a/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py
+++ b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py
@@ -415,9 +415,10 @@ def test_route_summarisation_with_summary_only_p1(request):
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)
+ 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")
@@ -614,7 +615,9 @@ def test_route_summarisation_with_summary_only_p1(request):
addr_type: {
"unicast": {
"advertise_networks": [
- {"network": NETWORK_4_1[addr_type],}
+ {
+ "network": NETWORK_4_1[addr_type],
+ }
]
}
}
@@ -1014,7 +1017,11 @@ def test_route_summarisation_with_as_set_p1(request):
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]):
+ for (
+ pfx,
+ seq_id,
+ network,
+ ) in zip([6, 7], [60, 70], [NETWORK_3_1, NETWORK_4_1]):
prefix_list = {
"r1": {
"prefix_lists": {
diff --git a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
index cf8be5f44f..c75055c26f 100644
--- a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
+++ b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
@@ -56,6 +56,7 @@ class TemplateTopo(Topo):
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r3"])
+
def setup_module(mod):
tgen = Topogen(TemplateTopo, mod.__name__)
tgen.start_topology()
@@ -114,6 +115,7 @@ def test_bgp_route():
assertmsg = '"r3" JSON output mismatches'
assert result is None, assertmsg
+
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
index 9106c163cd..6e7495d929 100644
--- 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
@@ -943,9 +943,10 @@ def test_modify_route_map_match_set_clauses_p1(request):
}
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)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error : Routes are still " "present {}".format(
+ tc_name, result
)
write_test_footer(tc_name)
diff --git a/tests/topotests/eigrp-topo1/test_eigrp_topo1.py b/tests/topotests/eigrp-topo1/test_eigrp_topo1.py
index 3ce1472ac0..bf94d39a4b 100644
--- a/tests/topotests/eigrp-topo1/test_eigrp_topo1.py
+++ b/tests/topotests/eigrp-topo1/test_eigrp_topo1.py
@@ -91,7 +91,7 @@ class NetworkTopo(Topo):
##
#####################################################
-
+@pytest.mark.eigrp
def setup_module(module):
"Setup topology"
tgen = Topogen(NetworkTopo, module.__name__)
diff --git a/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py b/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py
index 265124132f..d8c0cdc2fd 100644
--- a/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py
+++ b/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py
@@ -34,6 +34,8 @@ 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, "../"))
diff --git a/tests/topotests/example-test/test_template.py b/tests/topotests/example-test/test_template.py
index 4305e0199f..973303b830 100644
--- a/tests/topotests/example-test/test_template.py
+++ b/tests/topotests/example-test/test_template.py
@@ -44,6 +44,18 @@ from lib.topolog import logger
from mininet.topo import Topo
+#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
+"""
+
+
class TemplateTopo(Topo):
"Test topology builder"
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
index f24f463b8a..cd48716905 100755
--- 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
@@ -53,6 +53,19 @@ 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:
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
index 3ae3c9f4fe..0c72e30044 100755
--- 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
@@ -52,6 +52,19 @@ 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)
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
index 06fa2f4626..d05ad6db21 100755
--- 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
@@ -54,6 +54,19 @@ 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)
diff --git a/tests/topotests/isis-lfa-topo1/test_isis_lfa_topo1.py b/tests/topotests/isis-lfa-topo1/test_isis_lfa_topo1.py
index 67edcae90e..a655b418cf 100755
--- a/tests/topotests/isis-lfa-topo1/test_isis_lfa_topo1.py
+++ b/tests/topotests/isis-lfa-topo1/test_isis_lfa_topo1.py
@@ -62,7 +62,7 @@ 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, '../'))
+sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
# Import topogen and topotest helpers
@@ -76,8 +76,10 @@ from mininet.topo import Topo
# Global multi-dimensional dictionary containing all expected outputs
outputs = {}
+
class TemplateTopo(Topo):
"Test topology builder"
+
def build(self, *_args, **_opts):
"Build function"
tgen = get_topogen(self)
@@ -85,59 +87,58 @@ class TemplateTopo(Topo):
#
# Define FRR Routers
#
- for router in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6', 'rt7']:
+ 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")
+ 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']:
+ files = ["show_ipv6_route.ref", "show_yang_interface_isis_adjacencies.ref"]
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
outputs[rname] = {}
for step in range(1, 13 + 1):
outputs[rname][step] = {}
for file in files:
if step == 1:
# Get snapshots relative to the expected initial network convergence
- filename = '{}/{}/step{}/{}'.format(CWD, rname, step, file)
+ filename = "{}/{}/step{}/{}".format(CWD, rname, step, file)
outputs[rname][step][file] = open(filename).read()
else:
if rname != "rt1":
@@ -146,20 +147,23 @@ class TemplateTopo(Topo):
continue
# Get diff relative to the previous step
- filename = '{}/{}/step{}/{}.diff'.format(CWD, rname, step, file)
+ filename = "{}/{}/step{}/{}.diff".format(CWD, rname, step, file)
# Create temporary files in order to apply the diff
f_in = tempfile.NamedTemporaryFile()
f_in.write(outputs[rname][step - 1][file])
f_in.flush()
f_out = tempfile.NamedTemporaryFile()
- os.system("patch -s -o %s %s %s" %(f_out.name, f_in.name, filename))
+ 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()
+@pytest.mark.isis
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(TemplateTopo, mod.__name__)
@@ -170,16 +174,15 @@ def setup_module(mod):
# For all registered routers, load the zebra configuration file
for rname, router in router_list.iteritems():
router.load_config(
- TopoRouter.RD_ZEBRA,
- os.path.join(CWD, '{}/zebra.conf'.format(rname))
+ 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))
+ 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()
@@ -187,6 +190,7 @@ def teardown_module(mod):
# This function tears down the whole topology.
tgen.stop_topology()
+
def router_compare_json_output(rname, command, reference):
"Compare router JSON output"
@@ -196,12 +200,12 @@ def router_compare_json_output(rname, command, reference):
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)
+ 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
#
@@ -215,9 +219,13 @@ def test_isis_adjacencies_step1():
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"])
+ 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")
@@ -227,9 +235,11 @@ def test_rib_ipv6_step1():
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"])
+ 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
@@ -248,16 +258,28 @@ def test_rib_ipv6_step2():
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"')
+ 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"]
+ )
- for rname in ['rt1']:
- router_compare_json_output(rname, "show ipv6 route isis json",
- outputs[rname][2]["show_ipv6_route.ref"])
#
# Step 3
@@ -276,16 +298,28 @@ def test_rib_ipv6_step3():
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"')
+ 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"]
+ )
- for rname in ['rt1']:
- router_compare_json_output(rname, "show ipv6 route isis json",
- outputs[rname][3]["show_ipv6_route.ref"])
#
# Step 4
@@ -304,12 +338,16 @@ def test_rib_ipv6_step4():
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"')
+ 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"]
+ )
- for rname in ['rt1']:
- router_compare_json_output(rname, "show ipv6 route isis json",
- outputs[rname][4]["show_ipv6_route.ref"])
#
# Step 5
@@ -328,12 +366,16 @@ def test_rib_ipv6_step5():
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"')
+ 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"]
+ )
- for rname in ['rt1']:
- router_compare_json_output(rname, "show ipv6 route isis json",
- outputs[rname][5]["show_ipv6_route.ref"])
#
# Step 6
@@ -352,12 +394,16 @@ def test_rib_ipv6_step6():
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"')
+ 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"]
+ )
- for rname in ['rt1']:
- router_compare_json_output(rname, "show ipv6 route isis json",
- outputs[rname][6]["show_ipv6_route.ref"])
#
# Step 7
@@ -377,13 +423,19 @@ def test_rib_ipv6_step7():
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"')
+ 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"]
+ )
- for rname in ['rt1']:
- router_compare_json_output(rname, "show ipv6 route isis json",
- outputs[rname][7]["show_ipv6_route.ref"])
#
# Step 8
@@ -402,14 +454,22 @@ def test_rib_ipv6_step8():
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"')
+ 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"]
+ )
- for rname in ['rt1']:
- router_compare_json_output(rname, "show ipv6 route isis json",
- outputs[rname][8]["show_ipv6_route.ref"])
#
# Step 9
@@ -428,12 +488,16 @@ def test_rib_ipv6_step9():
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"')
+ 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"]
+ )
- for rname in ['rt1']:
- router_compare_json_output(rname, "show ipv6 route isis json",
- outputs[rname][9]["show_ipv6_route.ref"])
#
# Step 10
@@ -452,12 +516,20 @@ def test_rib_ipv6_step10():
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"')
+ 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"],
+ )
- for rname in ['rt1']:
- router_compare_json_output(rname, "show ipv6 route isis json",
- outputs[rname][10]["show_ipv6_route.ref"])
#
# Step 11
@@ -476,12 +548,18 @@ def test_rib_ipv6_step11():
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"')
+ 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"],
+ )
- for rname in ['rt1']:
- router_compare_json_output(rname, "show ipv6 route isis json",
- outputs[rname][11]["show_ipv6_route.ref"])
#
# Step 12
@@ -500,12 +578,18 @@ def test_rib_ipv6_step12():
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"')
+ 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"],
+ )
- for rname in ['rt1']:
- router_compare_json_output(rname, "show ipv6 route isis json",
- outputs[rname][12]["show_ipv6_route.ref"])
#
# Step 13
@@ -524,22 +608,29 @@ def test_rib_ipv6_step13():
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"')
+ 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"],
+ )
- for rname in ['rt1']:
- router_compare_json_output(rname, "show ipv6 route isis json",
- outputs[rname][13]["show_ipv6_route.ref"])
# Memory leak test template
def test_memory_leak():
"Run the memory leak test and report results."
tgen = get_topogen()
if not tgen.is_memleak_enabled():
- pytest.skip('Memory leak test/report is disabled')
+ pytest.skip("Memory leak test/report is disabled")
tgen.report_memory_leaks()
-if __name__ == '__main__':
+
+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 0000000000..e69de29bb2
--- /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 0000000000..90764a0d0f
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt1/isisd.conf
@@ -0,0 +1,27 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis sr-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis priority 100
+!
+router isis 1
+ net 49.0000.0000.0000.0001.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_ip_route.ref
new file mode 100644
index 0000000000..8557f4b010
--- /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 0000000000..fa76533756
--- /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 0000000000..26f0dffa7a
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,32 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-sw1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-lsp-bits-topo1/rt1/step2/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt1/step2/show_ip_route.ref
new file mode 100644
index 0000000000..c826efdcfe
--- /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 0000000000..a386b45dad
--- /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 0000000000..2b281b74fb
--- /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 0000000000..4b920eda01
--- /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 0000000000..8557f4b010
--- /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 0000000000..fa76533756
--- /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 0000000000..9d71d3005f
--- /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 0000000000..2bc4c4ad97
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt2/isisd.conf
@@ -0,0 +1,35 @@
+hostname rt2
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis sr-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+interface eth-rt4
+ ip router isis 2
+ ipv6 router isis 2
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0002.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
+router isis 2
+ net 49.0002.0000.0000.0002.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_ip_route.ref
new file mode 100644
index 0000000000..d7e886ce86
--- /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 0000000000..a92272f6d0
--- /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 0000000000..c70b44e1c9
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,58 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-sw1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 100,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-2",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-lsp-bits-topo1/rt2/zebra.conf b/tests/topotests/isis-lsp-bits-topo1/rt2/zebra.conf
new file mode 100644
index 0000000000..234e10efa9
--- /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 0000000000..9ad97109b5
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt3/isisd.conf
@@ -0,0 +1,35 @@
+hostname rt3
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis sr-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+interface eth-rt5
+ ip router isis 2
+ ipv6 router isis 2
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0003.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
+router isis 2
+ net 49.0002.0000.0000.0003.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_ip_route.ref
new file mode 100644
index 0000000000..55f0aedef5
--- /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 0000000000..5d6dfca76a
--- /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 0000000000..6950086b1e
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,51 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-sw1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 100,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-lsp-bits-topo1/rt3/zebra.conf b/tests/topotests/isis-lsp-bits-topo1/rt3/zebra.conf
new file mode 100644
index 0000000000..9a0defd62b
--- /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 0000000000..e85412a71d
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt4/isisd.conf
@@ -0,0 +1,42 @@
+hostname rt4
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis sr-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 4
+ ipv6 router isis 4
+ isis passive
+!
+interface eth-rt2
+ ip router isis 2
+ ipv6 router isis 2
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt5
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt6
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 2
+ net 49.0002.0000.0000.0004.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
+router isis 4
+ net 49.0004.0000.0000.0004.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_ip_route.ref
new file mode 100644
index 0000000000..2cf5c40635
--- /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 0000000000..cde7287943
--- /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 0000000000..233180ceb8
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,63 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-lsp-bits-topo1/rt4/zebra.conf b/tests/topotests/isis-lsp-bits-topo1/rt4/zebra.conf
new file mode 100644
index 0000000000..adcf433249
--- /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 0000000000..2cab0c88fc
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt5/isisd.conf
@@ -0,0 +1,42 @@
+hostname rt5
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis sr-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 2
+ ipv6 router isis 2
+ isis passive
+!
+interface eth-rt3
+ ip router isis 2
+ ipv6 router isis 2
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt4
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt6
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 2
+ net 49.0002.0000.0000.0005.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
+router isis 4
+ net 49.0004.0000.0000.0005.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_ip_route.ref
new file mode 100644
index 0000000000..fe34b03890
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_ip_route.ref
@@ -0,0 +1,118 @@
+{
+ "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",
+ "interfaceIndex":2,
+ "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",
+ "interfaceIndex":3,
+ "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 0000000000..7586c73852
--- /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 0000000000..f939a6abff
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,63 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt3",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-lsp-bits-topo1/rt5/zebra.conf b/tests/topotests/isis-lsp-bits-topo1/rt5/zebra.conf
new file mode 100644
index 0000000000..0f10ce921f
--- /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 0000000000..249f945e0c
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt6/isisd.conf
@@ -0,0 +1,32 @@
+hostname rt6
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis sr-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 4
+ ipv6 router isis 4
+ isis passive
+!
+interface eth-rt4
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt5
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 4
+ net 49.0004.0000.0000.0006.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_ip_route.ref
new file mode 100644
index 0000000000..2840514e6e
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_ip_route.ref
@@ -0,0 +1,107 @@
+{
+ "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",
+ "interfaceIndex":3,
+ "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 0000000000..278129f481
--- /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 0000000000..b4e8c23189
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-lsp-bits-topo1/rt6/step2/show_ip_route.ref b/tests/topotests/isis-lsp-bits-topo1/rt6/step2/show_ip_route.ref
new file mode 100644
index 0000000000..61efcb9e98
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step2/show_ip_route.ref
@@ -0,0 +1,100 @@
+{
+ "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",
+ "interfaceIndex":3,
+ "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 0000000000..2e982e0c37
--- /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 0000000000..8fecf14687
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step3/show_ip_route.ref
@@ -0,0 +1,80 @@
+{
+ "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",
+ "interfaceIndex":3,
+ "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 0000000000..9b53a1d760
--- /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 0000000000..2840514e6e
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/rt6/step4/show_ip_route.ref
@@ -0,0 +1,107 @@
+{
+ "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",
+ "interfaceIndex":3,
+ "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 0000000000..278129f481
--- /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 0000000000..6084010a93
--- /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 0000000000..95a0d87c33
--- /dev/null
+++ b/tests/topotests/isis-lsp-bits-topo1/test_isis_lsp_bits_topo1.py
@@ -0,0 +1,353 @@
+#!/usr/bin/env python
+
+#
+# test_isis_lsp_bits_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_isis_lsp_bits_topo1.py:
+
+ +---------+
+ | |
+ | RT1 |
+ | 1.1.1.1 |
+ | L1 |
+ +---------+
+ |eth-sw1
+ |
+ |
+ |
+ +---------+ | +---------+
+ | | | | |
+ | RT2 |eth-sw1 | eth-sw1| RT3 |
+ | 2.2.2.2 +----------+----------+ 3.3.3.3 |
+ | L1|L2 | 10.0.1.0/24 | L1|L2 |
+ +---------+ +---------+
+ eth-rt4| eth-rt5|
+ | |
+ 10.0.2.0/24| |10.0.4.0/24
+ | |
+ eth-rt2| eth-rt3|
+ +---------+ +---------+
+ | | | |
+ | RT4 | 10.0.6.0/24 | RT5 |
+ | 4.4.4.4 +---------------------+ 5.5.5.5 |
+ | L1|L2 |eth-rt5 eth-rt4| L1|L2 |
+ +---------+ +---------+
+ eth-rt6| |eth-rt6
+ | |
+ 10.0.7.0/24| |10.0.8.0/24
+ | +---------+ |
+ | | | |
+ | | RT6 | |
+ +----------+ 6.6.6.6 +-----------+
+ eth-rt4| L1 |eth-rt5
+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import re
+import tempfile
+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.
+from mininet.topo import Topo
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ #
+ # 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(TemplateTopo, 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 0000000000..e69de29bb2
--- /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 0000000000..a80f30dc7b
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/isisd.conf
@@ -0,0 +1,39 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis fast-reroute lfa
+ isis fast-reroute remote-lfa tunnel mpls-ldp
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis fast-reroute lfa
+ isis fast-reroute remote-lfa tunnel mpls-ldp
+!
+ip prefix-list PLIST seq 5 permit 10.0.255.8/32
+!
+router isis 1
+ net 49.0000.0000.0000.0001.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+ fast-reroute remote-lfa prefix-list PLIST
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt1/ldpd.conf
new file mode 100644
index 0000000000..f60fdb9742
--- /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 0000000000..680b31eb8d
--- /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 0000000000..c487d2740d
--- /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 0000000000..3fe2b798a0
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ip_route.ref.diff
new file mode 100644
index 0000000000..ef5707f14a
--- /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 0000000000..acd2ce003a
--- /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 0000000000..f7f31ac021
--- /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 0000000000..e980031ad7
--- /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 0000000000..f3ed764f0b
--- /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 0000000000..57b0b1de1a
--- /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 0000000000..107a0ba2f7
--- /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 0000000000..9cf24082e1
--- /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 0000000000..09469501f5
--- /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 0000000000..70fb1a65c7
--- /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 0000000000..4e4a5692a4
--- /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 0000000000..c9ebb1e3f5
--- /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 0000000000..e69de29bb2
--- /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 0000000000..e69de29bb2
--- /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 0000000000..e69de29bb2
--- /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 0000000000..e69de29bb2
--- /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 0000000000..33eb6577bd
--- /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 0000000000..7aaca3354e
--- /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 0000000000..741fc2d02b
--- /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 0000000000..7b4c6c50b9
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt2/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt2
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0002.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt2/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt2/ldpd.conf
new file mode 100644
index 0000000000..0a815ef004
--- /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 0000000000..657c69bf28
--- /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 0000000000..17d58a9d15
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt3/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt3
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0003.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt3/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt3/ldpd.conf
new file mode 100644
index 0000000000..40f1f5587a
--- /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 0000000000..86f5d2871a
--- /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 0000000000..1519fd4c16
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt4/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt4
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0004.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt4/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt4/ldpd.conf
new file mode 100644
index 0000000000..569ecf733e
--- /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 0000000000..1dd09bf83b
--- /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 0000000000..caf7477073
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt5/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt5
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt7
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0005.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt5/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt5/ldpd.conf
new file mode 100644
index 0000000000..519c3d3628
--- /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 0000000000..7117a2a2e3
--- /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 0000000000..cdf6267236
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt6/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt6
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt8
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0006.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt6/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt6/ldpd.conf
new file mode 100644
index 0000000000..a5b7062bec
--- /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 0000000000..c6344870b7
--- /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 0000000000..8ab8fcb232
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt7/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt7
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt8
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0007.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt7/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt7/ldpd.conf
new file mode 100644
index 0000000000..26d428c4c6
--- /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 0000000000..4c5e0f1126
--- /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 0000000000..abdc6a53a5
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt8/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt8
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt7
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0008.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt8/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt8/ldpd.conf
new file mode 100644
index 0000000000..1629f82de1
--- /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 0000000000..f3f10f649a
--- /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 0000000000..bb43e6b114
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/test_isis_rlfa_topo1.py
@@ -0,0 +1,662 @@
+#!/usr/bin/env python
+
+#
+# test_isis_rlfa_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_isis_rlfa_topo1.py:
+
+ +---------+ +---------+
+ | | | |
+ | RT1 | | RT2 |
+ | +---------------------+ |
+ | | | |
+ +---+-----+ +------+--+
+ | |
+ | |
+ | |
+ +---+-----+ +------+--+
+ | | | |
+ | RT3 | | RT4 |
+ | | | |
+ | | | |
+ +---+-----+ +------+--+
+ | |
+ | |
+ | |
+ +---+-----+ +------+--+
+ | | | |
+ | RT5 | | RT6 |
+ | | | |
+ | | | |
+ +---+-----+ +------+--+
+ | |
+ | |
+ | |
+ +---+-----+ +------+--+
+ | | | |
+ | RT7 | | RT8 |
+ | +---------------------+ |
+ | | | |
+ +---------+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import re
+import tempfile
+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.
+from mininet.topo import Topo
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ #
+ # 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()
+ f_in.write(outputs[rname][step - 1][file])
+ f_in.flush()
+ f_out = tempfile.NamedTemporaryFile()
+ 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()
+
+@pytest.mark.isis
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.iteritems():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_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-sr-te-topo1/dst/zebra.conf b/tests/topotests/isis-sr-te-topo1/dst/zebra.conf
new file mode 100644
index 0000000000..e873ac8a5c
--- /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 0000000000..efc03701b5
--- /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 0000000000..70ae1b07f5
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt1/isisd.conf
@@ -0,0 +1,30 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis sr-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0001.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.1/32 index 10
+ segment-routing prefix 2001:db8:1000::1/128 index 11
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt1/pathd.conf b/tests/topotests/isis-sr-te-topo1/rt1/pathd.conf
new file mode 100644
index 0000000000..911971496e
--- /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 0000000000..d4b27d157d
--- /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 0000000000..5fe58d0824
--- /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 0000000000..4ef8d946f2
--- /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 0000000000..9b28f6a42b
--- /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 0000000000..9b28f6a42b
--- /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 0000000000..249117198a
--- /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 0000000000..21f71f1254
--- /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 0000000000..3635c89efb
--- /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 0000000000..5712d210d4
--- /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 0000000000..5a76246e50
--- /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_inactive_srte.ref b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref
new file mode 100644
index 0000000000..09d5958305
--- /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 0000000000..e26039b835
--- /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 0000000000..01505c0318
--- /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 0000000000..9d71d3005f
--- /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 0000000000..733f26bc62
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt2/isisd.conf
@@ -0,0 +1,41 @@
+hostname rt2
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis sr-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+interface eth-rt4-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt4-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0002.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 2.2.2.2/32 index 20 no-php-flag
+ segment-routing prefix 2001:db8:1000::2/128 index 21 no-php-flag
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt2/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt2/zebra.conf
new file mode 100644
index 0000000000..dcb0686dc2
--- /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 0000000000..2395906cbf
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt3/isisd.conf
@@ -0,0 +1,41 @@
+hostname rt3
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis sr-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+interface eth-rt5-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt5-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0003.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 3.3.3.3/32 index 30 no-php-flag
+ segment-routing prefix 2001:db8:1000::3/128 index 31 no-php-flag
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt3/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt3/zebra.conf
new file mode 100644
index 0000000000..3254529386
--- /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 0000000000..07a7867cbb
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt4/isisd.conf
@@ -0,0 +1,48 @@
+hostname rt4
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis sr-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt2-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0004.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 4.4.4.4/32 index 40 no-php-flag
+ segment-routing prefix 2001:db8:1000::4/128 index 41 no-php-flag
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt4/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt4/zebra.conf
new file mode 100644
index 0000000000..4945897e9d
--- /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 0000000000..b0fcdede07
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt5/isisd.conf
@@ -0,0 +1,48 @@
+hostname rt5
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis sr-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt3-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt3-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0005.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 5.5.5.5/32 index 50 no-php-flag
+ segment-routing prefix 2001:db8:1000::5/128 index 51 no-php-flag
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt5/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt5/zebra.conf
new file mode 100644
index 0000000000..4cfea1a59f
--- /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 0000000000..e72ee52fce
--- /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 0000000000..3be24ad24c
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/rt6/isisd.conf
@@ -0,0 +1,36 @@
+hostname rt6
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis sr-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0006.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 6.6.6.6/32 index 60
+ segment-routing prefix 2001:db8:1000::6/128 index 61
+!
diff --git a/tests/topotests/isis-sr-te-topo1/rt6/pathd.conf b/tests/topotests/isis-sr-te-topo1/rt6/pathd.conf
new file mode 100644
index 0000000000..3bada7147c
--- /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 0000000000..2bb000346f
--- /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 0000000000..348f7761eb
--- /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 0000000000..241c80bdd7
--- /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 0000000000..20ea69e386
--- /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 0000000000..20ea69e386
--- /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 0000000000..10cafe9091
--- /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 0000000000..95bf995e2e
--- /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 0000000000..32c6e6c4e0
--- /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 0000000000..bfd2f92a28
--- /dev/null
+++ b/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py
@@ -0,0 +1,532 @@
+#!/usr/bin/env python
+
+#
+# test_isis_sr_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_isis_sr_te_topo1.py:
+
+ +---------+
+ | |
+ | RT1 |
+ | 1.1.1.1 |
+ | |
+ +---------+
+ |eth-sw1
+ |
+ |
+ |
+ +---------+ | +---------+
+ | | | | |
+ | RT2 |eth-sw1 | eth-sw1| RT3 |
+ | 2.2.2.2 +----------+----------+ 3.3.3.3 |
+ | | 10.0.1.0/24 | |
+ +---------+ +---------+
+ eth-rt4-1| |eth-rt4-2 eth-rt5-1| |eth-rt5-2
+ | | | |
+ 10.0.2.0/24| |10.0.3.0/24 10.0.4.0/24| |10.0.5.0/24
+ | | | |
+ eth-rt2-1| |eth-rt2-2 eth-rt3-1| |eth-rt3-2
+ +---------+ +---------+
+ | | | |
+ | RT4 | 10.0.6.0/24 | RT5 |
+ | 4.4.4.4 +---------------------+ 5.5.5.5 |
+ | |eth-rt5 eth-rt4| |
+ +---------+ +---------+
+ eth-rt6| |eth-rt6
+ | |
+ 10.0.7.0/24| |10.0.8.0/24
+ | +---------+ |
+ | | | |
+ | | RT6 | |
+ +----------+ 6.6.6.6 +-----------+
+ eth-rt4| |eth-rt5
+ +---------+
+ |eth-dst (.1)
+ |
+ |10.0.11.0/24
+ |
+ |eth-rt6 (.2)
+ +---------+
+ | |
+ | DST |
+ | 9.9.9.2 |
+ | |
+ +---------+
+
+"""
+
+import os
+import sys
+import pytest
+import json
+import re
+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.
+from mininet.topo import Topo
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ #
+ # 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")
+
+@pytest.mark.isis
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ tgen = Topogen(TemplateTopo, 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.iteritems():
+ 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 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 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)
+
+#
+# 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)
+
+#
+# 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_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/test_isis_sr_topo1.py b/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py
index 34eb6d90f6..63be3f78aa 100644
--- a/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py
+++ b/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py
@@ -134,7 +134,7 @@ class TemplateTopo(Topo):
switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6")
switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5")
-
+@pytest.mark.isis
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(TemplateTopo, mod.__name__)
diff --git a/tests/topotests/isis-tilfa-topo1/test_isis_tilfa_topo1.py b/tests/topotests/isis-tilfa-topo1/test_isis_tilfa_topo1.py
index 514ea53552..83751fabcd 100755
--- a/tests/topotests/isis-tilfa-topo1/test_isis_tilfa_topo1.py
+++ b/tests/topotests/isis-tilfa-topo1/test_isis_tilfa_topo1.py
@@ -74,7 +74,7 @@ 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, '../'))
+sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
# Import topogen and topotest helpers
@@ -88,8 +88,10 @@ from mininet.topo import Topo
# Global multi-dimensional dictionary containing all expected outputs
outputs = {}
+
class TemplateTopo(Topo):
"Test topology builder"
+
def build(self, *_args, **_opts):
"Build function"
tgen = get_topogen(self)
@@ -97,80 +99,85 @@ class TemplateTopo(Topo):
#
# Define FRR Routers
#
- for router in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']:
+ 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("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("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("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("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("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("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("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("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']:
+ files = [
+ "show_ip_route.ref",
+ "show_ipv6_route.ref",
+ "show_mpls_table.ref",
+ "show_yang_interface_isis_adjacencies.ref",
+ ]
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
outputs[rname] = {}
for step in range(1, 9 + 1):
outputs[rname][step] = {}
for file in files:
if step == 1:
# Get snapshots relative to the expected initial network convergence
- filename = '{}/{}/step{}/{}'.format(CWD, rname, step, file)
+ 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)
+ filename = "{}/{}/step{}/{}.diff".format(CWD, rname, step, file)
# Create temporary files in order to apply the diff
f_in = tempfile.NamedTemporaryFile()
f_in.write(outputs[rname][step - 1][file])
f_in.flush()
f_out = tempfile.NamedTemporaryFile()
- os.system("patch -s -o %s %s %s" %(f_out.name, f_in.name, filename))
+ 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()
+@pytest.mark.isis
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(TemplateTopo, mod.__name__)
@@ -181,16 +188,15 @@ def setup_module(mod):
# 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))
+ 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))
+ 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()
@@ -198,6 +204,7 @@ def teardown_module(mod):
# This function tears down the whole topology.
tgen.stop_topology()
+
def router_compare_json_output(rname, command, reference):
"Compare router JSON output"
@@ -207,12 +214,12 @@ def router_compare_json_output(rname, command, reference):
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)
+ 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
#
@@ -226,9 +233,13 @@ def test_isis_adjacencies_step1():
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"])
+ 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")
@@ -238,9 +249,11 @@ def test_rib_ipv4_step1():
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"])
+ 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")
@@ -250,9 +263,11 @@ def test_rib_ipv6_step1():
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"])
+ 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")
@@ -262,9 +277,11 @@ def test_mpls_lib_step1():
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"])
+ 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
@@ -283,12 +300,16 @@ def test_rib_ipv4_step2():
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"')
+ 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"]
+ )
- 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")
@@ -298,9 +319,11 @@ def test_rib_ipv6_step2():
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"])
+ 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")
@@ -310,9 +333,11 @@ def test_mpls_lib_step2():
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"])
+ 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
@@ -331,12 +356,16 @@ def test_rib_ipv4_step3():
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"')
+ 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"]
+ )
- 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")
@@ -346,9 +375,11 @@ def test_rib_ipv6_step3():
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"])
+ 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")
@@ -358,9 +389,11 @@ def test_mpls_lib_step3():
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"])
+ 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
@@ -384,12 +417,16 @@ def test_rib_ipv4_step4():
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"')
+ 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"]
+ )
- 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")
@@ -399,9 +436,11 @@ def test_rib_ipv6_step4():
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"])
+ 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")
@@ -411,9 +450,11 @@ def test_mpls_lib_step4():
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"])
+ 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
@@ -432,12 +473,14 @@ def test_rib_ipv4_step5():
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"')
+ 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"]
+ )
- 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")
@@ -447,9 +490,11 @@ def test_rib_ipv6_step5():
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"])
+ 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")
@@ -459,9 +504,11 @@ def test_mpls_lib_step5():
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"])
+ 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
@@ -480,12 +527,16 @@ def test_rib_ipv4_step6():
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"')
+ 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"]
+ )
- 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")
@@ -495,9 +546,11 @@ def test_rib_ipv6_step6():
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"])
+ 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")
@@ -507,9 +560,11 @@ def test_mpls_lib_step6():
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"])
+ 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
@@ -529,13 +584,19 @@ def test_rib_ipv4_step7():
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"')
+ 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"]
+ )
- 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")
@@ -545,9 +606,11 @@ def test_rib_ipv6_step7():
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"])
+ 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")
@@ -557,9 +620,11 @@ def test_mpls_lib_step7():
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"])
+ 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
@@ -578,13 +643,19 @@ def test_rib_ipv4_step8():
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"')
+ 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"]
+ )
- 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")
@@ -594,9 +665,11 @@ def test_rib_ipv6_step8():
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"])
+ 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")
@@ -606,9 +679,11 @@ def test_mpls_lib_step8():
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"])
+ 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
@@ -628,13 +703,19 @@ def test_rib_ipv4_step9():
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"')
+ 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"]
+ )
- 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")
@@ -644,9 +725,11 @@ def test_rib_ipv6_step9():
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"])
+ 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")
@@ -656,19 +739,22 @@ def test_mpls_lib_step9():
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"])
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][9]["show_mpls_table.ref"]
+ )
+
# Memory leak test template
def test_memory_leak():
"Run the memory leak test and report results."
tgen = get_topogen()
if not tgen.is_memleak_enabled():
- pytest.skip('Memory leak test/report is disabled')
+ pytest.skip("Memory leak test/report is disabled")
tgen.report_memory_leaks()
-if __name__ == '__main__':
+
+if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py
index 12121e4ddf..a79c8a268b 100644
--- a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py
+++ b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py
@@ -82,7 +82,7 @@ class ISISTopo1(Topo):
sw.add_link(tgen.gears["r4"])
sw.add_link(tgen.gears["r5"])
-
+@pytest.mark.isis
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(ISISTopo1, mod.__name__)
diff --git a/tests/topotests/isis-topo1/test_isis_topo1.py b/tests/topotests/isis-topo1/test_isis_topo1.py
index 71005a0362..25116d9452 100644
--- a/tests/topotests/isis-topo1/test_isis_topo1.py
+++ b/tests/topotests/isis-topo1/test_isis_topo1.py
@@ -84,7 +84,7 @@ class ISISTopo1(Topo):
sw.add_link(tgen.gears["r4"])
sw.add_link(tgen.gears["r5"])
-
+@pytest.mark.isis
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(ISISTopo1, mod.__name__)
diff --git a/tests/topotests/ldp-topo1/test_ldp_topo1.py b/tests/topotests/ldp-topo1/test_ldp_topo1.py
index 31adeafbf6..dfe65f010e 100644
--- a/tests/topotests/ldp-topo1/test_ldp_topo1.py
+++ b/tests/topotests/ldp-topo1/test_ldp_topo1.py
@@ -159,7 +159,7 @@ class NetworkTopo(Topo):
##
#####################################################
-
+@pytest.mark.ldp
def setup_module(module):
global topo, net
global fatal_error
@@ -647,9 +647,11 @@ def test_zebra_ipv4_routingTable():
else:
print("r%s ok" % i)
- assert failures == 0, (
- "IPv4 Zebra Routing Table verification failed for router r%s:\n%s"
- % (i, diff)
+ assert (
+ failures == 0
+ ), "IPv4 Zebra Routing Table verification failed for router r%s:\n%s" % (
+ i,
+ diff,
)
# Make sure that all daemons are running
diff --git a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
index ba94cd47d4..d659acb470 100644
--- a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
+++ b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
@@ -121,7 +121,8 @@ class TemplateTopo(Topo):
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r3"])
-
+@pytest.mark.ldp
+@pytest.mark.ospf
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(TemplateTopo, mod.__name__)
diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py
index ddeaf55b33..22602cb460 100644
--- a/tests/topotests/lib/bgp.py
+++ b/tests/topotests/lib/bgp.py
@@ -21,6 +21,7 @@
from copy import deepcopy
from time import sleep
import traceback
+import ipaddr
import ipaddress
import os
import sys
@@ -691,8 +692,8 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
config_data.append("{} activate".format(neigh_cxt))
disable_connected = peer.setdefault("disable_connected_check", False)
- keep_alive = peer.setdefault("keepalivetimer", 60)
- hold_down = peer.setdefault("holddowntimer", 180)
+ keep_alive = peer.setdefault("keepalivetimer", 3)
+ hold_down = peer.setdefault("holddowntimer", 10)
password = peer.setdefault("password", None)
no_password = peer.setdefault("no_password", None)
max_hop_limit = peer.setdefault("ebgp_multihop", 1)
@@ -1615,8 +1616,6 @@ def clear_bgp(tgen, addr_type, router, vrf=None):
else:
run_frr_cmd(rnode, "clear bgp *")
- sleep(5)
-
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
@@ -2115,8 +2114,8 @@ def verify_bgp_attributes(
errormsg(str) or True
"""
- logger.debug("Entering lib API: verify_bgp_attributes()")
- for router, rnode in tgen.routers().items():
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for router, rnode in tgen.routers().iteritems():
if router != dut:
continue
@@ -2129,7 +2128,9 @@ def verify_bgp_attributes(
dict_to_test = []
tmp_list = []
- if "route_maps" in input_dict.values()[0]:
+ 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:
@@ -2194,7 +2195,7 @@ def verify_bgp_attributes(
)
return errormsg
- logger.debug("Exiting lib API: verify_bgp_attributes()")
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return True
@@ -2514,8 +2515,9 @@ def verify_best_path_as_per_admin_distance(
return True
-@retry(attempts=6, wait=2, return_is_str=True)
-def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None):
+@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2)
+def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None,
+aspath=None, multi_nh=None):
"""
This API is to verify whether bgp rib has any
matching route for a nexthop.
@@ -2550,6 +2552,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None)
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:
@@ -2616,44 +2619,73 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None)
st_found = True
found_routes.append(st_rt)
- if next_hop:
+ if next_hop and multi_nh and st_found:
if not isinstance(next_hop, 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)
+ 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,
- )
- errormsg = (
- "Nexthop {} is Missing for "
- "route {} in RIB of router {}\n".format(
+ 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,
)
- )
return errormsg
else:
nh_found = True
+
+ elif next_hop and multi_nh is None:
+ if not isinstance(next_hop, 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"
@@ -3676,7 +3708,6 @@ def verify_attributes_for_evpn_routes(
"""
API to verify rd and rt value using "sh bgp l2vpn evpn 10.1.1.1"
command.
-
Parameters
----------
* `tgen`: topogen object
@@ -3690,7 +3721,6 @@ def verify_attributes_for_evpn_routes(
* `ipLen` : IP prefix length
* `rd_peer` : Peer name from which RD will be auto-generated
* `rt_peer` : Peer name from which RT will be auto-generated
-
Usage
-----
input_dict_1 = {
@@ -4067,7 +4097,6 @@ def verify_evpn_routes(
"""
API to verify evpn routes using "sh bgp l2vpn evpn"
command.
-
Parameters
----------
* `tgen`: topogen object
@@ -4078,7 +4107,6 @@ def verify_evpn_routes(
* `route_type` : Route type 5 is supported as of now
* `EthTag` : Ethernet tag, by-default is 0
* `next_hop` : Prefered nexthop for the evpn routes
-
Usage
-----
input_dict_1 = {
@@ -4091,7 +4119,6 @@ def verify_evpn_routes(
}
}
result = verify_evpn_routes(tgen, topo, input_dict)
-
Returns
-------
errormsg(str) or True
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index 6c24b6ddbb..3f360ef40a 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -31,7 +31,6 @@ from re import search as re_search
from tempfile import mkdtemp
import os
-import io
import sys
import traceback
import socket
@@ -39,6 +38,7 @@ import ipaddress
import platform
if sys.version_info[0] > 2:
+ import io
import configparser
else:
import StringIO
@@ -151,8 +151,8 @@ class InvalidCLIError(Exception):
def run_frr_cmd(rnode, cmd, isjson=False):
"""
- Execute frr show commands in priviledged mode
- * `rnode`: router node on which commands needs to executed
+ 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:
@@ -184,11 +184,11 @@ def apply_raw_config(tgen, input_dict):
"""
API to configure raw configuration on device. This can be used for any cli
- which does has not been implemented in JSON.
+ which has not been implemented in JSON.
Parameters
----------
- * `tgen`: tgen onject
+ * `tgen`: tgen object
* `input_dict`: configuration that needs to be applied
Usage
@@ -232,8 +232,8 @@ def create_common_configuration(
frr_json.conf and load to router
Parameters
----------
- * `tgen`: tgen onject
- * `data`: Congiguration data saved in a list.
+ * `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.
@@ -257,6 +257,8 @@ def create_common_configuration(
"bgp": "! BGP Config\n",
"vrf": "! VRF Config\n",
"ospf": "! OSPF Config\n",
+ "ospf6": "! OSPF Config\n",
+ "pim": "! PIM Config\n",
}
)
@@ -292,8 +294,8 @@ def create_common_configuration(
def kill_router_daemons(tgen, router, daemons):
"""
- Router's current config would be saved to /etc/frr/ for each deamon
- and deamon would be killed forcefully using SIGKILL.
+ 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
@@ -389,6 +391,8 @@ def check_router_status(tgen):
daemons.append("bgpd")
if "zebra" in result:
daemons.append("zebra")
+ if "pimd" in result:
+ daemons.append("pimd")
rnode.startDaemons(daemons)
@@ -593,13 +597,13 @@ def load_config_to_router(tgen, routerName, save_bkup=False):
def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None):
"""
- API to get the link local ipv6 address of a perticular interface using
+ API to get the link local ipv6 address of a particular interface using
FRR command 'show interface'
- * `tgen`: tgen onject
- * `router` : router for which hightest interface should be
+ * `tgen`: tgen object
+ * `router` : router for which highest interface should be
calculated
- * `intf` : interface for which linklocal address needs to be taken
+ * `intf` : interface for which link-local address needs to be taken
* `vrf` : VRF name
Usage
@@ -688,7 +692,7 @@ def generate_support_bundle():
def start_topology(tgen, daemon=None):
"""
Starting topology, create tmp files which are loaded to routers
- to start deamons and then start routers
+ to start daemons and then start routers
* `tgen` : topogen object
"""
@@ -696,7 +700,7 @@ def start_topology(tgen, daemon=None):
# Starting topology
tgen.start_topology()
- # Starting deamons
+ # Starting daemons
router_list = tgen.routers()
ROUTER_LIST = sorted(
@@ -735,28 +739,41 @@ def start_topology(tgen, daemon=None):
except IOError as err:
logger.error("I/O error({0}): {1}".format(err.errno, err.strerror))
- # Loading empty zebra.conf file to router, to start the zebra deamon
+ # Loading empty zebra.conf file to router, to start the zebra daemon
router.load_config(
TopoRouter.RD_ZEBRA, "{}/{}/zebra.conf".format(TMPDIR, rname)
)
- # Loading empty bgpd.conf file to router, to start the bgp deamon
+ # Loading empty bgpd.conf file to router, to start the bgp daemon
router.load_config(TopoRouter.RD_BGP, "{}/{}/bgpd.conf".format(TMPDIR, rname))
if daemon and "ospfd" in daemon:
- # Loading empty ospf.conf file to router, to start the bgp deamon
+ # Loading empty ospf.conf file to router, to start the bgp daemon
router.load_config(
TopoRouter.RD_OSPF, "{}/{}/ospfd.conf".format(TMPDIR, rname)
)
- # Starting routers
+
+ if daemon and "ospf6d" in daemon:
+ # Loading empty ospf.conf file to router, to start the bgp daemon
+ router.load_config(
+ TopoRouter.RD_OSPF6, "{}/{}/ospf6d.conf".format(TMPDIR, rname)
+ )
+
+ if daemon and "pimd" in daemon:
+ # Loading empty pimd.conf file to router, to start the pim deamon
+ router.load_config(
+ TopoRouter.RD_PIM, "{}/{}/pimd.conf".format(TMPDIR, 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 /etc/frr/ for each deamon
- and router and its deamons would be stopped.
+ 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
@@ -774,8 +791,8 @@ def stop_router(tgen, router):
def start_router(tgen, router):
"""
- Router will started and config would be loaded from /etc/frr/ for each
- deamon
+ Router will be started and config would be loaded from /tmp/topotest/<suite>/<router> for each
+ daemon
* `tgen` : topogen object
* `router`: Device under test
@@ -786,8 +803,8 @@ def start_router(tgen, router):
try:
router_list = tgen.routers()
- # Router and its deamons would be started and config would
- # be loaded to router for each deamon from /etc/frr
+ # 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
@@ -835,9 +852,204 @@ def topo_daemons(tgen, topo):
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")
+ 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 = tgen.routers()[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
+ cmd = "vconfig add {} {}".format(interface, vlan)
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+ vlan_intf = "{}.{}".format(interface, vlan)
+
+ ip = data["ip"]
+ subnet = data["subnet"]
+
+ # Bringing interface up
+ cmd = "ip link set up {}".format(vlan_intf)
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+ # Assigning IP address
+ cmd = "ifconfig {} {} netmask {}".format(vlan_intf, ip, subnet)
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+
+def tcpdump_capture_start(
+ tgen,
+ router,
+ intf,
+ protocol=None,
+ grepstr=None,
+ timeout=0,
+ options=None,
+ cap_file=None,
+ background=True,
+):
+ """
+ API to capture network packets using tcp dump.
+
+ Packages used :
+
+ Parameters
+ ----------
+ * `tgen`: topogen object.
+ * `router`: router on which ping has to be performed.
+ * `intf` : interface for capture.
+ * `protocol` : protocol for which packet needs to be captured.
+ * `grepstr` : string to filter out tcp dump output.
+ * `timeout` : Time for which packet needs to be captured.
+ * `options` : options for TCP dump, all tcpdump options can be used.
+ * `cap_file` : filename to store capture dump.
+ * `background` : Make tcp dump run in back ground.
+
+ Usage
+ -----
+ tcpdump_result = tcpdump_dut(tgen, 'r2', intf, protocol='tcp', timeout=20,
+ options='-A -vv -x > r2bgp.txt ')
+ Returns
+ -------
+ 1) True for successful capture
+ 2) errormsg - when tcp dump fails
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ rnode = tgen.routers()[router]
+
+ if timeout > 0:
+ cmd = "timeout {}".format(timeout)
+ else:
+ cmd = ""
+
+ cmdargs = "{} tcpdump".format(cmd)
+
+ if intf:
+ cmdargs += " -i {}".format(str(intf))
+ if protocol:
+ cmdargs += " {}".format(str(protocol))
+ if options:
+ cmdargs += " -s 0 {}".format(str(options))
+
+ if cap_file:
+ file_name = os.path.join(LOGDIR, tgen.modname, router, cap_file)
+ cmdargs += " -w {}".format(str(file_name))
+ # Remove existing capture file
+ rnode.run("rm -rf {}".format(file_name))
+
+ if grepstr:
+ cmdargs += ' | grep "{}"'.format(str(grepstr))
+
+ logger.info("Running tcpdump command: [%s]", cmdargs)
+ if not background:
+ rnode.run(cmdargs)
+ else:
+ rnode.run("nohup {} & /dev/null 2>&1".format(cmdargs))
+
+ # Check if tcpdump process is running
+ if background:
+ result = rnode.run("pgrep tcpdump")
+ logger.debug("ps -ef | grep tcpdump \n {}".format(result))
+
+ if not result:
+ errormsg = "tcpdump is not running {}".format("tcpdump")
+ return errormsg
+ else:
+ logger.info("Packet capture started on %s: interface %s", router, intf)
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def tcpdump_capture_stop(tgen, router):
+ """
+ API to capture network packets using tcp dump.
+
+ Packages used :
+
+ Parameters
+ ----------
+ * `tgen`: topogen object.
+ * `router`: router on which ping has to be performed.
+ * `intf` : interface for capture.
+ * `protocol` : protocol for which packet needs to be captured.
+ * `grepstr` : string to filter out tcp dump output.
+ * `timeout` : Time for which packet needs to be captured.
+ * `options` : options for TCP dump, all tcpdump options can be used.
+ * `cap2file` : filename to store capture dump.
+ * `bakgrnd` : Make tcp dump run in back ground.
+
+ Usage
+ -----
+ tcpdump_result = tcpdump_dut(tgen, 'r2', intf, protocol='tcp', timeout=20,
+ options='-A -vv -x > r2bgp.txt ')
+ Returns
+ -------
+ 1) True for successful capture
+ 2) errormsg - when tcp dump fails
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ rnode = tgen.routers()[router]
+
+ # Check if tcpdump process is running
+ result = rnode.run("ps -ef | grep tcpdump")
+ logger.debug("ps -ef | grep tcpdump \n {}".format(result))
+
+ if not re_search(r"{}".format("tcpdump"), result):
+ errormsg = "tcpdump is not running {}".format("tcpdump")
+ return errormsg
+ else:
+ ppid = tgen.net.nameToNode[rnode.name].pid
+ rnode.run("set +m; pkill -P %s tcpdump &> /dev/null" % ppid)
+ logger.info("Stopped tcpdump capture")
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
#############################################
# Common APIs, will be used by all protocols
#############################################
@@ -1137,11 +1349,11 @@ 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]
@@ -1152,14 +1364,20 @@ def generate_ips(network, no_of_ips):
mask = int(start_ipaddr.split("/")[1])
else:
logger.debug("start_ipaddr {} must have a / in it".format(start_ipaddr))
- assert(0)
+ assert 0
addr_type = validate_ip_address(start_ip)
if addr_type == "ipv4":
- start_ip = ipaddress.IPv4Address(frr_unicode(start_ip))
+ 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(unicode(start_ip))
step = 2 ** (32 - mask)
if addr_type == "ipv6":
- start_ip = ipaddress.IPv6Address(frr_unicode(start_ip))
+ 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(unicode(start_ip))
step = 2 ** (128 - mask)
next_ip = start_ip
@@ -1181,7 +1399,7 @@ def find_interface_with_greater_ip(topo, router, loopback=True, interface=True):
it will return highest IP from loopback IPs otherwise from physical
interface IPs.
* `topo` : json file data
- * `router` : router for which hightest interface should be calculated
+ * `router` : router for which highest interface should be calculated
"""
link_data = topo["routers"][router]["links"]
@@ -1287,7 +1505,6 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict
_wait = kwargs.pop("wait", wait)
_attempts = kwargs.pop("attempts", attempts)
_attempts = int(_attempts)
- expected = True
if _attempts < 0:
raise ValueError("attempts must be 0 or greater")
@@ -1297,12 +1514,10 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict
_return_is_str = kwargs.pop("return_is_str", return_is_str)
_return_is_dict = kwargs.pop("return_is_str", return_is_dict)
+ _expected = kwargs.setdefault("expected", True)
+ kwargs.pop("expected")
for i in range(1, _attempts + 1):
try:
- _expected = kwargs.setdefault("expected", True)
- if _expected is False:
- expected = _expected
- kwargs.pop("expected")
ret = func(*args, **kwargs)
logger.debug("Function returned %s", ret)
if _return_is_str and isinstance(ret, bool) and _expected:
@@ -1314,11 +1529,11 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict
if _return_is_dict and isinstance(ret, dict):
return ret
- if _attempts == i and expected:
+ if _attempts == i:
generate_support_bundle()
return ret
except Exception as err:
- if _attempts == i and expected:
+ if _attempts == i:
generate_support_bundle()
logger.info("Max number of attempts (%r) reached", _attempts)
raise
@@ -1360,6 +1575,17 @@ def step(msg, reset=False):
_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
#############################################
@@ -1378,6 +1604,25 @@ def create_interfaces_cfg(tgen, topo, build=False):
-------
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)
@@ -1424,66 +1669,26 @@ def create_interfaces_cfg(tgen, topo, build=False):
else:
interface_data.append("ipv6 address {}\n".format(intf_addr))
+ ospf_keywords = [
+ "hello_interval",
+ "dead_interval",
+ "network",
+ "priority",
+ "cost",
+ ]
if "ospf" in data:
- ospf_data = data["ospf"]
- if "area" in ospf_data:
- intf_ospf_area = c_data["links"][destRouterLink]["ospf"]["area"]
- if "delete" in data and data["delete"]:
- interface_data.append("no ip ospf area")
- else:
- interface_data.append(
- "ip ospf area {}".format(intf_ospf_area)
- )
-
- if "hello_interval" in ospf_data:
- intf_ospf_hello = c_data["links"][destRouterLink]["ospf"][
- "hello_interval"
- ]
- if "delete" in data and data["delete"]:
- interface_data.append("no ip ospf " " hello-interval")
- else:
- interface_data.append(
- "ip ospf " " hello-interval {}".format(intf_ospf_hello)
- )
-
- if "dead_interval" in ospf_data:
- intf_ospf_dead = c_data["links"][destRouterLink]["ospf"][
- "dead_interval"
- ]
- if "delete" in data and data["delete"]:
- interface_data.append("no ip ospf" " dead-interval")
- else:
- interface_data.append(
- "ip ospf " " dead-interval {}".format(intf_ospf_dead)
- )
-
- if "network" in ospf_data:
- intf_ospf_nw = c_data["links"][destRouterLink]["ospf"][
- "network"
- ]
- if "delete" in data and data["delete"]:
- interface_data.append(
- "no ip ospf" " network {}".format(intf_ospf_nw)
- )
- else:
- interface_data.append(
- "ip ospf" " network {}".format(intf_ospf_nw)
- )
-
- if "priority" in ospf_data:
- intf_ospf_nw = c_data["links"][destRouterLink]["ospf"][
- "priority"
- ]
+ 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
+ )
- if "delete" in data and data["delete"]:
- interface_data.append("no ip ospf" " priority")
- else:
- interface_data.append(
- "ip ospf" " priority {}".format(intf_ospf_nw)
- )
result = create_common_configuration(
tgen, c_router, interface_data, "interface_config", build=build
)
+
except InvalidCLIError:
# Traceback
errormsg = traceback.format_exc()
@@ -2222,9 +2427,9 @@ def shutdown_bringup_interface(tgen, dut, intf_name, ifaceaction=False):
-----
dut = "r3"
intf = "r3-r1-eth0"
- # Shut down ineterface
+ # Shut down interface
shutdown_bringup_interface(tgen, dut, intf, False)
- # Bring up ineterface
+ # Bring up interface
shutdown_bringup_interface(tgen, dut, intf, True)
Returns
-------
@@ -2233,13 +2438,58 @@ def shutdown_bringup_interface(tgen, dut, intf_name, ifaceaction=False):
router_list = tgen.routers()
if ifaceaction:
- logger.info("Bringing up interface : {}".format(intf_name))
+ logger.info("Bringing up interface {} : {}".format(dut, intf_name))
else:
- logger.info("Shutting down interface : {}".format(intf_name))
+ logger.info("Shutting down interface {} : {}".format(dut, intf_name))
interface_set_status(router_list[dut], intf_name, ifaceaction)
+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()
+
+ except Exception as e:
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: start_router()")
+
+
def addKernelRoute(
tgen, router, intf, group_addr_range, next_hop=None, src=None, del_action=None
):
@@ -2250,7 +2500,7 @@ def addKernelRoute(
-----------
* `tgen` : Topogen object
* `router`: router for which kernal routes needs to be added
- * `intf`: interface name, for which kernal 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
@@ -2313,7 +2563,7 @@ def configure_vxlan(tgen, input_dict):
"""
Add and configure vxlan
- * `tgen`: tgen onject
+ * `tgen`: tgen object
* `input_dict` : data for vxlan config
Usage:
@@ -2414,7 +2664,7 @@ def configure_brctl(tgen, topo, input_dict):
"""
Add and configure brctl
- * `tgen`: tgen onject
+ * `tgen`: tgen object
* `input_dict` : data for brctl config
Usage:
@@ -2508,7 +2758,7 @@ def configure_interface_mac(tgen, input_dict):
"""
Add and configure brctl
- * `tgen`: tgen onject
+ * `tgen`: tgen object
* `input_dict` : data for mac config
input_mac= {
@@ -2562,7 +2812,7 @@ def verify_rib(
tag=None,
metric=None,
fib=None,
- count_only=False
+ count_only=False,
):
"""
Data will be read from input_dict or input JSON file, API will generate
@@ -2754,8 +3004,10 @@ def verify_rib(
"Nexthops are missing for "
"route {} in RIB of router {}: "
"expected {}, found {}\n".format(
- st_rt, dut, len(next_hop),
- len(found_hops)
+ st_rt,
+ dut,
+ len(next_hop),
+ len(found_hops),
)
)
return errormsg
@@ -2802,7 +3054,11 @@ def verify_rib(
errormsg = (
"[DUT: {}]: tag value {}"
" is not matched for"
- " route {} in RIB \n".format(dut, _tag, st_rt,)
+ " route {} in RIB \n".format(
+ dut,
+ _tag,
+ st_rt,
+ )
)
return errormsg
@@ -2819,7 +3075,11 @@ def verify_rib(
errormsg = (
"[DUT: {}]: metric value "
"{} is not matched for "
- "route {} in RIB \n".format(dut, metric, st_rt,)
+ "route {} in RIB \n".format(
+ dut,
+ metric,
+ st_rt,
+ )
)
return errormsg
@@ -2868,7 +3128,9 @@ def verify_rib(
for advertise_network_dict in advertise_network:
if "vrf" in advertise_network_dict:
- cmd = "{} vrf {} json".format(command, advertise_network_dict["vrf"])
+ cmd = "{} vrf {} json".format(
+ command, advertise_network_dict["vrf"]
+ )
else:
cmd = "{} json".format(command)
@@ -2947,6 +3209,7 @@ def verify_rib(
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return True
+
@retry(attempts=6, wait=2, return_is_str=True)
def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None):
"""
@@ -3327,7 +3590,12 @@ def verify_prefix_lists(tgen, input_dict):
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 = (
@@ -3550,7 +3818,6 @@ def verify_cli_json(tgen, input_dict):
"""
API to verify if JSON is available for clis
command.
-
Parameters
----------
* `tgen`: topogen object
@@ -3720,7 +3987,6 @@ def verify_vrf_vni(tgen, input_dict):
"""
API to verify vrf vni details using "show vrf vni json"
command.
-
Parameters
----------
* `tgen`: topogen object
@@ -3852,3 +4118,269 @@ def required_linux_kernel_version(required_version):
)
return error_msg
return True
+
+
+def iperfSendIGMPJoin(
+ tgen, server, bindToAddress, l4Type="UDP", join_interval=1, inc_step=0, repeat=0
+):
+ """
+ Run iperf to send IGMP join and traffic
+
+ Parameters:
+ -----------
+ * `tgen` : Topogen object
+ * `l4Type`: string, one of [ TCP, UDP ]
+ * `server`: iperf server, from where IGMP join would be sent
+ * `bindToAddress`: bind to <host>, an interface or multicast
+ address
+ * `join_interval`: seconds between periodic bandwidth reports
+ * `inc_step`: increamental steps, by default 0
+ * `repeat`: Repetition of group, by default 0
+
+ returns:
+ --------
+ errormsg or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ rnode = tgen.routers()[server]
+
+ iperfArgs = "iperf -s "
+
+ # UDP/TCP
+ if l4Type == "UDP":
+ iperfArgs += "-u "
+
+ iperfCmd = iperfArgs
+ # Group address range to cover
+ if bindToAddress:
+ if type(bindToAddress) is not list:
+ Address = []
+ start = ipaddress.IPv4Address(frr_unicode(bindToAddress))
+
+ Address = [start]
+ next_ip = start
+
+ count = 1
+ while count < repeat:
+ next_ip += inc_step
+ Address.append(next_ip)
+ count += 1
+ bindToAddress = Address
+
+ for bindTo in bindToAddress:
+ iperfArgs = iperfCmd
+ iperfArgs += "-B %s " % bindTo
+
+ # Join interval
+ if join_interval:
+ iperfArgs += "-i %d " % join_interval
+
+ iperfArgs += " &>/dev/null &"
+ # Run iperf command to send IGMP join
+ logger.debug("[DUT: {}]: Running command: [{}]".format(server, iperfArgs))
+ output = rnode.run("set +m; {} sleep 0.5".format(iperfArgs))
+
+ # Check if iperf process is running
+ if output:
+ pid = output.split()[1]
+ rnode.run("touch /var/run/frr/iperf_server.pid")
+ rnode.run("echo %s >> /var/run/frr/iperf_server.pid" % pid)
+ else:
+ errormsg = "IGMP join is not sent for {}. Error: {}".format(bindTo, output)
+ logger.error(output)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def iperfSendTraffic(
+ tgen,
+ client,
+ bindToAddress,
+ ttl,
+ time=0,
+ l4Type="UDP",
+ inc_step=0,
+ repeat=0,
+ mappedAddress=None,
+):
+ """
+ Run iperf to send IGMP join and traffic
+
+ Parameters:
+ -----------
+ * `tgen` : Topogen object
+ * `l4Type`: string, one of [ TCP, UDP ]
+ * `client`: iperf client, from where iperf traffic would be sent
+ * `bindToAddress`: bind to <host>, an interface or multicast
+ address
+ * `ttl`: time to live
+ * `time`: time in seconds to transmit for
+ * `inc_step`: increamental steps, by default 0
+ * `repeat`: Repetition of group, by default 0
+ * `mappedAddress`: Mapped Interface ip address
+
+ returns:
+ --------
+ errormsg or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ rnode = tgen.routers()[client]
+
+ iperfArgs = "iperf -c "
+
+ iperfCmd = iperfArgs
+ # Group address range to cover
+ if bindToAddress:
+ if type(bindToAddress) is not list:
+ Address = []
+ start = ipaddress.IPv4Address(frr_unicode(bindToAddress))
+
+ Address = [start]
+ next_ip = start
+
+ count = 1
+ while count < repeat:
+ next_ip += inc_step
+ Address.append(next_ip)
+ count += 1
+ bindToAddress = Address
+
+ for bindTo in bindToAddress:
+ iperfArgs = iperfCmd
+ iperfArgs += "%s " % bindTo
+
+ # Mapped Interface IP
+ if mappedAddress:
+ iperfArgs += "-B %s " % mappedAddress
+
+ # UDP/TCP
+ if l4Type == "UDP":
+ iperfArgs += "-u -b 0.012m "
+
+ # TTL
+ if ttl:
+ iperfArgs += "-T %d " % ttl
+
+ # Time
+ if time:
+ iperfArgs += "-t %d " % time
+
+ iperfArgs += " &>/dev/null &"
+
+ # Run iperf command to send multicast traffic
+ logger.debug("[DUT: {}]: Running command: [{}]".format(client, iperfArgs))
+ output = rnode.run("set +m; {} sleep 0.5".format(iperfArgs))
+
+ # Check if iperf process is running
+ if output:
+ pid = output.split()[1]
+ rnode.run("touch /var/run/frr/iperf_client.pid")
+ rnode.run("echo %s >> /var/run/frr/iperf_client.pid" % pid)
+ else:
+ errormsg = "Multicast traffic is not sent for {}. Error {}".format(
+ bindTo, output
+ )
+ logger.error(output)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def kill_iperf(tgen, dut=None, action=None):
+ """
+ Killing iperf process if running for any router in topology
+ Parameters:
+ -----------
+ * `tgen` : Topogen object
+ * `dut` : Any iperf hostname to send igmp prune
+ * `action`: to kill igmp join iperf action is remove_join
+ to kill traffic iperf action is remove_traffic
+
+ Usage
+ ----
+ kill_iperf(tgen, dut ="i6", action="remove_join")
+
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for router, rnode in router_list.items():
+ # Run iperf command to send IGMP join
+ pid_client = rnode.run("cat /var/run/frr/iperf_client.pid")
+ pid_server = rnode.run("cat /var/run/frr/iperf_server.pid")
+ if action == "remove_join":
+ pids = pid_server
+ elif action == "remove_traffic":
+ pids = pid_client
+ else:
+ pids = "\n".join([pid_client, pid_server])
+ for pid in pids.split("\n"):
+ pid = pid.strip()
+ if pid.isdigit():
+ cmd = "set +m; kill -9 %s &> /dev/null" % pid
+ logger.debug("[DUT: {}]: Running command: [{}]".format(router, cmd))
+ rnode.run(cmd)
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+
+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()")
+
+ for router in input_dict.keys():
+ if router not in tgen.routers():
+ continue
+
+ rnode = tgen.routers()[router]
+ nh_list = input_dict[router]
+
+ if validate_ip_address(nh_list.keys()[0]) is "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:
+ 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
diff --git a/tests/topotests/lib/lutil.py b/tests/topotests/lib/lutil.py
index 9cbea67af1..0b6a946fda 100644
--- a/tests/topotests/lib/lutil.py
+++ b/tests/topotests/lib/lutil.py
@@ -23,6 +23,7 @@ import time
import datetime
import json
import math
+import time
from lib.topolog import logger
from mininet.net import Mininet
@@ -194,8 +195,9 @@ Total %-4d %-4d %d\n\
if op != "wait":
self.l_line += 1
self.log(
- "(#%d) %s:%s COMMAND:%s:%s:%s:%s:%s:"
+ "%s (#%d) %s:%s COMMAND:%s:%s:%s:%s:%s:"
% (
+ time.asctime(),
self.l_total + 1,
self.l_filename,
self.l_line,
diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py
index 9f3d4841b0..5bc9f14fea 100644
--- a/tests/topotests/lib/ospf.py
+++ b/tests/topotests/lib/ospf.py
@@ -62,7 +62,7 @@ def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=Tru
"r1": {
"ospf": {
"router_id": "22.22.22.22",
- "area": [{ "id":0.0.0.0, "type": "nssa"}]
+ "area": [{ "id": "0.0.0.0", "type": "nssa"}]
}
}
@@ -94,7 +94,7 @@ def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=Tru
return result
-def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True):
+def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True, ospf="ospf"):
"""
Helper API to create ospf global configuration.
@@ -105,6 +105,33 @@ def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True
* `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
-------
@@ -115,17 +142,17 @@ def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True
logger.debug("Entering lib API: __create_ospf_global()")
try:
- ospf_data = input_dict[router]["ospf"]
+ ospf_data = input_dict[router][ospf]
del_ospf_action = ospf_data.setdefault("delete", False)
if del_ospf_action:
- config_data = ["no router ospf"]
+ config_data = ["no router {}".format(ospf)]
result = create_common_configuration(
- tgen, router, config_data, "ospf", build, load_config
+ tgen, router, config_data, ospf, build, load_config
)
return result
config_data = []
- cmd = "router ospf"
+ cmd = "router {}".format(ospf)
config_data.append(cmd)
@@ -133,9 +160,9 @@ def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True
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 ospf router-id")
+ config_data.append("no {} router-id".format(ospf))
if router_id:
- config_data.append("ospf router-id {}".format(router_id))
+ config_data.append("{} router-id {}".format(ospf, router_id))
# redistribute command
redistribute_data = ospf_data.setdefault("redistribute", {})
@@ -154,6 +181,7 @@ def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True
if del_action:
cmd = "no {}".format(cmd)
config_data.append(cmd)
+
# area information
area_data = ospf_data.setdefault("area", {})
if area_data:
@@ -172,9 +200,20 @@ def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True
if del_action:
cmd = "no {}".format(cmd)
config_data.append(cmd)
- result = create_common_configuration(
- tgen, router, config_data, "ospf", build, load_config
- )
+
+ # area interface information for ospf6d only
+ if ospf == "ospf6":
+ area_iface = ospf_data.setdefault("neighbors", {})
+ if area_iface:
+ for neighbor in area_iface:
+ if "area" in area_iface[neighbor]:
+ iface = input_dict[router]["links"][neighbor]["interface"]
+ cmd = "interface {} area {}".format(
+ iface, area_iface[neighbor]["area"]
+ )
+ if area_iface[neighbor].setdefault("delete", False):
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
# summary information
summary_data = ospf_data.setdefault("summary-address", {})
@@ -200,8 +239,9 @@ def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True
if del_action:
cmd = "no {}".format(cmd)
config_data.append(cmd)
+
result = create_common_configuration(
- tgen, router, config_data, "ospf", build, load_config
+ tgen, router, config_data, ospf, build, load_config
)
except InvalidCLIError:
@@ -238,7 +278,7 @@ def create_router_ospf6(tgen, topo, input_dict=None, build=False, load_config=Tr
-------
True or False
"""
- logger.debug("Entering lib API: create_router_ospf()")
+ logger.debug("Entering lib API: create_router_ospf6()")
result = False
if not input_dict:
@@ -247,67 +287,15 @@ def create_router_ospf6(tgen, topo, input_dict=None, build=False, load_config=Tr
topo = topo["routers"]
input_dict = deepcopy(input_dict)
for router in input_dict.keys():
- if "ospf" not in input_dict[router]:
- logger.debug("Router %s: 'ospf' not present in input_dict", router)
+ if "ospf6" not in input_dict[router]:
+ logger.debug("Router %s: 'ospf6' not present in input_dict", router)
continue
- result = __create_ospf_global(tgen, input_dict, router, build, load_config)
-
- logger.debug("Exiting lib API: create_router_ospf()")
- return result
-
-
-def __create_ospf6_global(tgen, input_dict, router, build=False, load_config=True):
- """
- Helper API to create ospf 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
- -------
- True or False
- """
-
- result = False
- logger.debug("Entering lib API: __create_ospf_global()")
- try:
-
- ospf_data = input_dict[router]["ospf6"]
- del_ospf_action = ospf_data.setdefault("delete", False)
- if del_ospf_action:
- config_data = ["no ipv6 router ospf"]
- result = create_common_configuration(
- tgen, router, config_data, "ospf", build, load_config
- )
- return result
-
- config_data = []
- cmd = "router ospf"
-
- config_data.append(cmd)
-
- 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 ospf router-id")
- if router_id:
- config_data.append("ospf router-id {}".format(router_id))
-
- result = create_common_configuration(
- tgen, router, config_data, "ospf", build, load_config
+ result = __create_ospf_global(
+ tgen, input_dict, router, build, load_config, "ospf6"
)
- except InvalidCLIError:
- # Traceback
- errormsg = traceback.format_exc()
- logger.error(errormsg)
- return errormsg
- logger.debug("Exiting lib API: create_ospf_global()")
+ logger.debug("Exiting lib API: create_router_ospf6()")
return result
@@ -330,7 +318,7 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config=
"links": {
"r2": {
"ospf": {
- "authentication": 'message-digest',
+ "authentication": "message-digest",
"authentication-key": "ospf",
"message-digest-key": "10"
}
@@ -379,6 +367,7 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_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":
@@ -464,6 +453,32 @@ def clear_ospf(tgen, router):
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
################################
@@ -525,7 +540,7 @@ def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
logger.info("Verifying OSPF neighborship on router %s:", router)
show_ospf_json = run_frr_cmd(
- rnode, "show ip ospf neighbor all json", isjson=True
+ rnode, "show ip ospf neighbor all json", isjson=True
)
# Verifying output dictionary show_ospf_json is empty or not
@@ -658,6 +673,70 @@ def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
return result
+################################
+# Verification procs
+################################
+@retry(attempts=40, wait=2, return_is_str=True)
+def verify_ospf6_neighbor(tgen, topo):
+ """
+ This API is to verify ospf neighborship by running
+ show ip ospf neighbour command,
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+
+ Usage
+ -----
+ Check FULL neighbors.
+ verify_ospf_neighbor(tgen, topo)
+
+ result = verify_ospf_neighbor(tgen, topo)
+
+ Returns
+ -------
+ True or False (Error Message)
+ """
+
+ logger.debug("Entering lib API: verify_ospf6_neighbor()")
+ result = False
+ for router, rnode in tgen.routers().items():
+ if "ospf6" not in topo["routers"][router]:
+ continue
+
+ logger.info("Verifying OSPF6 neighborship on router %s:", router)
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ipv6 ospf6 neighbor json", isjson=True
+ )
+
+ if not show_ospf_json:
+ return "OSPF6 is not running"
+
+ ospf_nbr_list = topo["routers"][router]["ospf6"]["neighbors"]
+ no_of_peer = 0
+ for ospf_nbr in ospf_nbr_list:
+ ospf_nbr_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"]
+ for neighbor in show_ospf_json["neighbors"]:
+ if neighbor["neighborId"] == ospf_nbr_rid:
+ nh_state = neighbor["state"]
+ break
+ else:
+ return "[DUT: {}] OSPF6 peer {} missing".format(router, data_rid)
+
+ if nh_state == "Full":
+ no_of_peer += 1
+
+ if no_of_peer == len(ospf_nbr_list):
+ logger.info("[DUT: {}] OSPF6 is Converged".format(router))
+ result = True
+ else:
+ return "[DUT: {}] OSPF6 is not Converged".format(router)
+
+ logger.debug("Exiting API: verify_ospf6_neighbor()")
+ return result
+
+
@retry(attempts=21, wait=2, return_is_str=True)
def verify_ospf_rib(
tgen, dut, input_dict, next_hop=None, tag=None, metric=None, fib=None
@@ -847,19 +926,23 @@ def verify_ospf_rib(
if "routeType" not in ospf_rib_json[st_rt]:
errormsg = (
"[DUT: {}]: routeType missing"
- "for route {} in OSPF RIB \n".format(dut, st_rt)
+ " 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)
+ " 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)
+ "[DUT: {}]: Found routeType {}"
+ " for route {}".format(dut, _rtype, st_rt)
)
if tag:
if "tag" not in ospf_rib_json[st_rt]:
@@ -874,7 +957,11 @@ def verify_ospf_rib(
errormsg = (
"[DUT: {}]: tag value {}"
" is not matched for"
- " route {} in RIB \n".format(dut, _tag, st_rt,)
+ " route {} in RIB \n".format(
+ dut,
+ _tag,
+ st_rt,
+ )
)
return errormsg
@@ -891,7 +978,11 @@ def verify_ospf_rib(
errormsg = (
"[DUT: {}]: metric value "
"{} is not matched for "
- "route {} in RIB \n".format(dut, metric, st_rt,)
+ "route {} in RIB \n".format(
+ dut,
+ metric,
+ st_rt,
+ )
)
return errormsg
diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py
new file mode 100644
index 0000000000..294f60bf68
--- /dev/null
+++ b/tests/topotests/lib/pim.py
@@ -0,0 +1,3450 @@
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+
+import sys
+import os
+import re
+import datetime
+import traceback
+import pytest
+from time import sleep
+from copy import deepcopy
+from lib.topolog import logger
+
+# Import common_config to use commomnly used APIs
+from lib.common_config import (
+ create_common_configuration,
+ InvalidCLIError,
+ retry,
+ run_frr_cmd,
+)
+
+####
+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 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": {
+ "disable" : ["l1-i1-eth1"],
+ "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
+ }]
+ }
+ }
+ }
+
+
+ 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():
+ result = _enable_disable_pim(tgen, topo, input_dict, router, build)
+
+ if "pim" not in input_dict[router]:
+ logger.debug("Router %s: 'pim' is not present in " "input_dict", router)
+ continue
+
+ if result is True:
+ if "rp" not in input_dict[router]["pim"]:
+ continue
+
+ result = _create_pim_config(
+ tgen, topo, input_dict, router, build, load_config
+ )
+ if result is not True:
+ return False
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+def _create_pim_config(tgen, topo, input_dict, router, build=False, load_config=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
+ * `router` : router id to be configured.
+ * `build` : Only for initial setup phase this is set as True.
+
+ Returns
+ -------
+ True or False
+ """
+
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ try:
+
+ pim_data = input_dict[router]["pim"]
+
+ for dut in tgen.routers():
+ if "pim" not in input_dict[router]:
+ continue
+
+ for destLink, data in topo[dut]["links"].items():
+ if "pim" not in data:
+ continue
+
+ if "rp" in pim_data:
+ config_data = []
+ rp_data = pim_data["rp"]
+
+ 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)
+
+ # 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:
+ cmd = "ip pim rp keep-alive-timer {}".format(keep_alive_timer)
+ config_data.append(cmd)
+
+ 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:
+ cmd = "ip pim rp {} {}".format(rp_addr, grp_addr)
+ config_data.append(cmd)
+
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if prefix_list:
+ cmd = "ip pim rp {} prefix-list {}".format(
+ rp_addr, prefix_list
+ )
+ config_data.append(cmd)
+
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ result = create_common_configuration(
+ tgen, dut, config_data, "pim", build, load_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 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)
+ 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)
+ cmd = "ip igmp"
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ del_attr = intf_data[intf_name]["igmp"].setdefault("delete_attr", False)
+ 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)
+ 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)
+ 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(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
+ -------
+ True or False
+ """
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ try:
+ config_data = []
+
+ enable_flag = True
+ # Disable pim on interface
+ 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")
+
+ # Enable pim on interface
+ if enable_flag:
+ 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")
+
+ result = create_common_configuration(
+ tgen, router, config_data, "interface_config", build=build
+ )
+ 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 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")
+
+ 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 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:
+
+ for dut in input_dict.keys():
+ if "pim" not in input_dict[dut]:
+ continue
+
+ pim_data = input_dict[dut]["pim"]
+
+ if "force_expire" in pim_data:
+ config_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)
+
+ result = create_common_configuration(
+ tgen, dut, config_data, "pim", build=build
+ )
+ 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
+
+
+#############################################
+# Verification APIs
+#############################################
+@retry(attempts=6, wait=2, return_is_str=True)
+def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None):
+ """
+ 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
+
+ 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"] == "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(attempts=21, wait=2, return_is_str=True)
+def verify_igmp_groups(tgen, dut, interface, group_addresses):
+ """
+ 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
+
+ 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(attempts=31, wait=2, return_is_str=True)
+def verify_upstream_iif(
+ tgen, dut, iif, src_address, group_addresses, joinState=None, refCount=1
+):
+ """
+ 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
+
+ Usage
+ -----
+ dut = "r1"
+ iif = "r1-r0-eth0"
+ src_address = "*"
+ group_address = "225.1.1.1"
+ result = verify_upstream_iif(tgen, dut, iif, src_address, group_address,
+ state, refCount)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ logger.info(
+ "[DUT: %s]: Verifying upstream Inbound Interface" " for IGMP groups received:",
+ dut,
+ )
+ 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]
+
+ if type(iif) is not list:
+ iif = [iif]
+
+ for grp_addr in group_addresses:
+ # Verify group address
+ if grp_addr not in show_ip_pim_upstream_json:
+ errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % (
+ dut,
+ grp_addr,
+ )
+ return errormsg
+ group_addr_json = show_ip_pim_upstream_json[grp_addr]
+
+ # Verify source address
+ if src_address not in group_addr_json:
+ errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % (
+ dut,
+ src_address,
+ grp_addr,
+ )
+ return errormsg
+
+ # Verify Inbound Interface
+ found = False
+ for in_interface in iif:
+ if group_addr_json[src_address]["inboundInterface"] == in_interface:
+ if refCount > 0:
+ logger.info(
+ "[DUT %s]: Verifying refCount "
+ "for (%s,%s) [PASSED]!! "
+ " Found Expected: %s",
+ dut,
+ src_address,
+ grp_addr,
+ group_addr_json[src_address]["refCount"],
+ )
+ found = True
+ if found:
+ if joinState is None:
+ if group_addr_json[src_address]["joinState"] != "Joined":
+ errormsg = (
+ "[DUT %s]: Verifying iif "
+ "(Inbound Interface) for (%s,%s) and"
+ " joinState :%s [FAILED]!! "
+ " Expected: %s, Found: %s"
+ % (
+ dut,
+ src_address,
+ grp_addr,
+ group_addr_json[src_address]["joinState"],
+ in_interface,
+ group_addr_json[src_address]["inboundInterface"],
+ )
+ )
+ return errormsg
+
+ elif group_addr_json[src_address]["joinState"] != joinState:
+ errormsg = (
+ "[DUT %s]: Verifying iif "
+ "(Inbound Interface) for (%s,%s) and"
+ " joinState :%s [FAILED]!! "
+ " Expected: %s, Found: %s"
+ % (
+ dut,
+ src_address,
+ grp_addr,
+ group_addr_json[src_address]["joinState"],
+ in_interface,
+ group_addr_json[src_address]["inboundInterface"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: Verifying iif(Inbound Interface)"
+ " for (%s,%s) and joinState is %s [PASSED]!! "
+ " Found Expected: (%s)",
+ dut,
+ src_address,
+ grp_addr,
+ group_addr_json[src_address]["joinState"],
+ group_addr_json[src_address]["inboundInterface"],
+ )
+ if not found:
+ errormsg = (
+ "[DUT %s]: Verifying iif "
+ "(Inbound Interface) for (%s, %s) "
+ "[FAILED]!! "
+ " Expected: %s, Found: %s"
+ % (
+ dut,
+ src_address,
+ grp_addr,
+ in_interface,
+ group_addr_json[src_address]["inboundInterface"],
+ )
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(attempts=6, wait=2, return_is_str=True)
+def verify_join_state_and_timer(tgen, dut, iif, src_address, group_addresses):
+ """
+ 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
+
+ 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,
+ )
+ 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 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(attempts=41, wait=2, return_is_dict=True)
+def verify_ip_mroutes(
+ tgen, dut, src_address, group_addresses, iif, oil, return_uptime=False, mwait=0
+):
+ """
+ Verify ip mroutes and make sure (*, G)/(S, G) is present in mroutes
+ by running "show ip pim upstream" 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
+
+
+ Usage
+ -----
+ dut = "r1"
+ group_address = "225.1.1.1"
+ result = verify_ip_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 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 ip mroute json", 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
+
+ if not isinstance(group_addresses, list):
+ group_addresses = [group_addresses]
+
+ if not isinstance(iif, list) and iif is not "none":
+ iif = [iif]
+
+ if not isinstance(oil, list) and oil is not "none":
+ oil = [oil]
+
+ 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(attempts=31, wait=2, return_is_str=True)
+def verify_pim_rp_info(
+ tgen, topo, dut, group_addresses, oif=None, rp=None, source=None, iamrp=None
+):
+ """
+ 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
+
+ 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]
+
+ 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 type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ if type(oif) is not list:
+ oif = [oif]
+
+ 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:
+ show_ip_route_json = run_frr_cmd(
+ rnode, "show ip route connected json", isjson=True
+ )
+ for _rp in show_ip_route_json.keys():
+ if rp == _rp.split("/")[0]:
+ iamRP = True
+ break
+ else:
+ iamRP = False
+
+ 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 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(attempts=31, wait=2, return_is_str=True)
+def verify_pim_state(
+ tgen, dut, iif, oil, group_addresses, src_address=None, installed_fl=None
+):
+ """
+ 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
+
+ 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)
+ show_pim_state_json = run_frr_cmd(rnode, "show ip pim state json", isjson=True)
+
+ if installed_fl is None:
+ installed_fl = 1
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ 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) ",
+ "Found: (iif: %s, oil: %s, installed: %s)"
+ % (
+ dut,
+ grp_addr,
+ iif,
+ oil,
+ "1",
+ 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 verify_pim_interface_traffic(tgen, input_dict):
+ """
+ Verify ip pim interface traffice by running
+ "show ip pim interface traffic" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict(dict)`: defines DUT, what and from which interfaces
+ traffic needs to be verified
+ 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)
+ 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
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return output_dict
+
+
+@retry(attempts=21, wait=2, return_is_str=True)
+def verify_pim_interface(tgen, topo, dut, interface=None, interface_ip=None):
+ """
+ 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
+
+ Usage
+ -----
+ result = verify_pim_interfacetgen, topo, dut, interface=ens192, interface_ip=20.1.1.1)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router in tgen.routers():
+ if router != dut:
+ continue
+
+ logger.info("[DUT: %s]: Verifying PIM interface status:", dut)
+
+ rnode = tgen.routers()[dut]
+ show_ip_pim_interface_json = rnode.\
+ vtysh_cmd("show ip pim interface json", isjson=True)
+
+ logger.info("show_ip_pim_interface_json: \n %s",
+ show_ip_pim_interface_json)
+
+ if interface_ip:
+ if interface in show_ip_pim_interface_json:
+ pim_intf_json = show_ip_pim_interface_json[interface]
+ if pim_intf_json["address"] != interface_ip:
+ errormsg = ("[DUT %s]: PIM interface "
+ "ip is not correct "
+ "[FAILED]!! Expected : %s, Found : %s"
+ %(dut, pim_intf_json["address"],interface_ip))
+ return errormsg
+ else:
+ logger.info("[DUT %s]: PIM interface "
+ "ip is correct "
+ "[Passed]!! Expected : %s, Found : %s"
+ %(dut, pim_intf_json["address"],interface_ip))
+ return True
+ else:
+ for destLink, data in topo["routers"][dut]["links"].items():
+ if "type" in data and data["type"] == "loopback":
+ continue
+
+ if "pim" in data and data["pim"] == "enable":
+ pim_interface = data["interface"]
+ pim_intf_ip = data["ipv4"].split("/")[0]
+
+ if pim_interface in show_ip_pim_interface_json:
+ pim_intf_json = show_ip_pim_interface_json\
+ [pim_interface]
+
+ # Verifying PIM interface
+ if pim_intf_json["address"] != pim_intf_ip and \
+ pim_intf_json["state"] != "up":
+ errormsg = ("[DUT %s]: PIM interface: %s "
+ "PIM interface ip: %s, status check "
+ "[FAILED]!! Expected : %s, Found : %s"
+ %(dut, pim_interface, pim_intf_ip,
+ pim_interface, pim_intf_json["state"]))
+ return errormsg
+
+ logger.info("[DUT %s]: PIM interface: %s, "
+ "interface ip: %s, status: %s"
+ " [PASSED]!!",
+ dut, pim_interface, pim_intf_ip,
+ pim_intf_json["state"])
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def clear_ip_pim_interface_traffic(tgen, topo):
+ """
+ Clear ip pim interface traffice by running
+ "clear ip pim interface traffic" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ Usage
+ -----
+
+ result = clear_ip_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_ip_pim_interfaces(tgen, dut):
+ """
+ Clear ip pim interface by running
+ "clear ip pim interfaces" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: Device Under Test
+ Usage
+ -----
+
+ result = clear_ip_pim_interfaces(tgen, dut)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ nh_before_clear = {}
+ nh_after_clear = {}
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verify pim neighbor before pim" " neighbor clear", dut)
+ # To add uptime initially
+ sleep(10)
+ run_json_before = run_frr_cmd(rnode, "show ip pim neighbor json", isjson=True)
+
+ for key, value in run_json_before.items():
+ if bool(value):
+ for _key, _value in value.items():
+ nh_before_clear[key] = _value["upTime"]
+
+ # Clearing PIM neighbors
+ logger.info("[DUT: %s]: Clearing pim interfaces", dut)
+ run_frr_cmd(rnode, "clear ip pim interfaces")
+
+ logger.info("[DUT: %s]: Verify pim neighbor after pim" " neighbor clear", dut)
+
+ found = False
+
+ # Waiting for maximum 60 sec
+ fail_intf = []
+ for retry in range(1, 13):
+ logger.info("[DUT: %s]: Waiting for 5 sec for PIM neighbors" " to come up", dut)
+ sleep(5)
+ run_json_after = run_frr_cmd(rnode, "show ip pim neighbor json", isjson=True)
+ found = True
+ for pim_intf in nh_before_clear.keys():
+ if pim_intf not in run_json_after or not run_json_after[pim_intf]:
+ found = False
+ fail_intf.append(pim_intf)
+
+ if found is True:
+ break
+ else:
+ errormsg = (
+ "[DUT: %s]: pim neighborship is not formed for %s"
+ "after clear_ip_pim_interfaces %s [FAILED!!]",
+ dut,
+ fail_intf,
+ )
+ return errormsg
+
+ for key, value in run_json_after.items():
+ if bool(value):
+ for _key, _value in value.items():
+ nh_after_clear[key] = _value["upTime"]
+
+ # Verify uptime for neighbors
+ for pim_intf in nh_before_clear.keys():
+ d1 = datetime.datetime.strptime(nh_before_clear[pim_intf], "%H:%M:%S")
+ d2 = datetime.datetime.strptime(nh_after_clear[pim_intf], "%H:%M:%S")
+ if d2 >= d1:
+ errormsg = (
+ "[DUT: %s]: PIM neighborship is not cleared for",
+ " interface %s [FAILED!!]",
+ dut,
+ pim_intf,
+ )
+
+ logger.info("[DUT: %s]: PIM neighborship is cleared [PASSED!!]")
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+def clear_ip_igmp_interfaces(tgen, dut):
+ """
+ Clear ip igmp interfaces by running
+ "clear ip igmp interfaces" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+
+ Usage
+ -----
+ dut = "r1"
+ result = clear_ip_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(attempts=10, wait=2, return_is_str=True)
+def clear_ip_mroute_verify(tgen, dut):
+ """
+ Clear ip mroute by running "clear ip mroute" cli and verify
+ mroutes are up again after mroute clear
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: Device Under Test
+ Usage
+ -----
+
+ result = clear_ip_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_ip_mroute(tgen, dut=None):
+ """
+ Clear ip mroute by running "clear ip mroute" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test, default None
+
+ Usage
+ -----
+ clear_ip_mroute(tgen, dut)
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for router, rnode in router_list.items():
+ if dut is not None and router != dut:
+ continue
+
+ logger.debug("[DUT: %s]: Clearing ip mroute", router)
+ rnode.vtysh_cmd("clear ip mroute")
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+
+def reconfig_interfaces(tgen, topo, senderRouter, receiverRouter, packet=None):
+ """
+ Configure interface ip for sender and receiver routers
+ as per bsr packet
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `senderRouter` : Sender router
+ * `receiverRouter` : Receiver router
+ * `packet` : BSR packet in raw format
+
+ Returns
+ -------
+ True or False
+ """
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ try:
+ config_data = []
+
+ src_ip = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["src_ip"]
+ dest_ip = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["dest_ip"]
+
+ for destLink, data in topo["routers"][senderRouter]["links"].items():
+ if "type" in data and data["type"] == "loopback":
+ continue
+
+ if "pim" in data and data["pim"] == "enable":
+ sender_interface = data["interface"]
+ sender_interface_ip = data["ipv4"]
+
+ config_data.append("interface {}".format(sender_interface))
+ config_data.append("no ip address {}".format(sender_interface_ip))
+ config_data.append("ip address {}".format(src_ip))
+
+ result = create_common_configuration(
+ tgen, senderRouter, config_data, "interface_config"
+ )
+ if result is not True:
+ return False
+
+ config_data = []
+ links = topo["routers"][destLink]["links"]
+ pim_neighbor = {key: links[key] for key in [senderRouter]}
+
+ data = pim_neighbor[senderRouter]
+ if "type" in data and data["type"] == "loopback":
+ continue
+
+ if "pim" in data and data["pim"] == "enable":
+ receiver_interface = data["interface"]
+ receiver_interface_ip = data["ipv4"]
+
+ config_data.append("interface {}".format(receiver_interface))
+ config_data.append("no ip address {}".format(receiver_interface_ip))
+ config_data.append("ip address {}".format(dest_ip))
+
+ result = create_common_configuration(
+ tgen, receiverRouter, config_data, "interface_config"
+ )
+ if result is not True:
+ return False
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: reconfig_interfaces()")
+ return result
+
+
+def add_rp_interfaces_and_pim_config(tgen, topo, interface, rp, rp_mapping):
+ """
+ Add physical interfaces tp RP for all the RPs
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `interface` : RP interface
+ * `rp` : rp for given topology
+ * `rp_mapping` : dictionary of all groups and RPs
+
+ Returns
+ -------
+ True or False
+ """
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ try:
+ config_data = []
+
+ for group, rp_list in rp_mapping.items():
+ for _rp in rp_list:
+ config_data.append("interface {}".format(interface))
+ config_data.append("ip address {}".format(_rp))
+ config_data.append("ip pim")
+
+ 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: add_rp_interfaces_and_pim_config()")
+ return result
+
+
+def scapy_send_bsr_raw_packet(
+ tgen, topo, senderRouter, receiverRouter, packet=None, interval=1, count=1
+):
+ """
+ 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
+ * `interval` : Interval between the packets
+ * `count` : Number of packets to be sent
+
+ returns:
+ --------
+ errormsg or True
+ """
+
+ global CWD
+ result = ""
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ rnode = tgen.routers()[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"]
+
+ if interval > 1 or count > 1:
+ cmd = (
+ "nohup /usr/bin/python {}/send_bsr_packet.py '{}' '{}' "
+ "--interval={} --count={} &".format(
+ CWD, packet, sender_interface, interval, count
+ )
+ )
+ else:
+ cmd = (
+ "/usr/bin/python {}/send_bsr_packet.py '{}' '{}' "
+ "--interval={} --count={}".format(
+ CWD, packet, sender_interface, interval, count
+ )
+ )
+
+ logger.info("Scapy cmd: \n %s", cmd)
+ result = rnode.run(cmd)
+
+ if result == "":
+ return result
+
+ 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(attempts=6, wait=2, return_is_str=True)
+def verify_pim_grp_rp_source(tgen, topo, dut, grp_addr, rp_source, rpadd=None):
+ """
+ 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
+
+ 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(attempts=31, wait=2, return_is_str=True)
+def verify_pim_bsr(tgen, topo, dut, bsr_ip):
+ """
+ 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
+
+ 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(attempts=31, wait=2, return_is_str=True)
+def verify_ip_pim_upstream_rpf(tgen, topo, dut, interface, group_addresses, rp=None):
+ """
+ Verify IP PIM upstream rpf, config is verified
+ using "show ip 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
+
+ Usage
+ -----
+ result = verify_ip_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: ip}
+ 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(attempts=31, wait=2, return_is_str=True)
+def verify_ip_pim_join(tgen, topo, dut, interface, group_addresses, src_address=None):
+ """
+ Verify ip pim join by running "show ip 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
+
+ Usage
+ -----
+ dut = "r1"
+ interface = "r1-r0-eth0"
+ group_address = "225.1.1.1"
+ result = verify_ip_pim_join(tgen, dut, star, group_address, interface)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying pim join", dut)
+ show_pim_join_json = run_frr_cmd(rnode, "show ip pim join json", isjson=True)
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ for grp_addr in group_addresses:
+ # Verify if IGMP is enabled in DUT
+ if "igmp" not in topo["routers"][dut]:
+ pim_join = True
+ else:
+ pim_join = False
+
+ interface_json = show_pim_join_json[interface]
+
+ grp_addr = grp_addr.split("/")[0]
+ for source, data in interface_json[grp_addr].items():
+
+ # Verify pim join
+ if pim_join:
+ if data["group"] == grp_addr and data["channelJoinName"] == "JOIN":
+ logger.info(
+ "[DUT %s]: Verifying pim join for group: %s"
+ "[PASSED]!! Found Expected: (%s)",
+ dut,
+ grp_addr,
+ data["channelJoinName"],
+ )
+ else:
+ errormsg = (
+ "[DUT %s]: Verifying pim join for group: %s"
+ "[FAILED]!! Expected: (%s) "
+ "Found: (%s)" % (dut, grp_addr, "JOIN", data["channelJoinName"])
+ )
+ return errormsg
+
+ if not pim_join:
+ if data["group"] == grp_addr and data["channelJoinName"] == "NOINFO":
+ logger.info(
+ "[DUT %s]: Verifying pim join for group: %s"
+ "[PASSED]!! Found Expected: (%s)",
+ dut,
+ grp_addr,
+ data["channelJoinName"],
+ )
+ else:
+ errormsg = (
+ "[DUT %s]: Verifying pim join for group: %s"
+ "[FAILED]!! Expected: (%s) "
+ "Found: (%s)"
+ % (dut, grp_addr, "NOINFO", data["channelJoinName"])
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(attempts=31, wait=2, return_is_dict=True)
+def verify_igmp_config(tgen, input_dict, stats_return=False):
+ """
+ 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
+
+ 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, interface), 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,
+ intf_detail_json["reportV2"],
+ )
+ )
+ 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(attempts=31, wait=2, return_is_str=True)
+def verify_pim_config(tgen, input_dict):
+ """
+ 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
+
+ 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(attempts=21, wait=2, return_is_dict=True)
+def verify_multicast_traffic(tgen, input_dict, return_traffic=False):
+ """
+ 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
+ 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(attempts=21, wait=2, return_is_str=True)
+def verify_multicast_flag_state(tgen, dut, src_address, group_addresses, flag):
+ """
+ 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
+
+ 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(attempts=21, wait=2, return_is_str=True)
+def verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip):
+ """
+ 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
+
+ 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
diff --git a/tests/topotests/lib/send_bsr_packet.py b/tests/topotests/lib/send_bsr_packet.py
new file mode 100755
index 0000000000..c226899324
--- /dev/null
+++ b/tests/topotests/lib/send_bsr_packet.py
@@ -0,0 +1,58 @@
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+import sys
+import argparse
+from scapy.all import Raw, sendp
+import binascii
+
+
+def send_packet(packet, iface, interval, count):
+ """
+ Read BSR packet in Raw format and send it to specified interface
+
+ Parameter:
+ ---------
+ * `packet` : BSR packet in raw format
+ * `interface` : Interface from which packet would be send
+ * `interval` : Interval between the packets
+ * `count` : Number of packets to be sent
+ """
+
+ data = binascii.a2b_hex(packet)
+ p = Raw(load=data)
+ p.show()
+ sendp(p, inter=int(interval), iface=iface, count=int(count))
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="Send BSR Raw packet")
+ parser.add_argument("packet", help="Packet in raw format")
+ parser.add_argument("iface", help="Packet send to this ineterface")
+ parser.add_argument("--interval", help="Interval between packets", default=0)
+ parser.add_argument(
+ "--count", help="Number of times packet is sent repetitively", default=0
+ )
+ args = parser.parse_args()
+
+ if not args.packet or not args.iface:
+ sys.exit(1)
+
+ send_packet(args.packet, args.iface, args.interval, args.count)
diff --git a/tests/topotests/lib/snmptest.py b/tests/topotests/lib/snmptest.py
new file mode 100644
index 0000000000..5112500e0b
--- /dev/null
+++ b/tests/topotests/lib/snmptest.py
@@ -0,0 +1,152 @@
+#
+# topogen.py
+# Library of helper functions for NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+SNMP library to test snmp walks and gets
+
+Basic usage instructions:
+
+* define an SnmpTester class giving a router, address, community and version
+* use test_oid or test_walk to check values in MIBS
+* see tests/topotest/simple-snmp-test/test_simple_snmp.py for example
+"""
+
+from topolog import logger
+
+
+class SnmpTester(object):
+ "A helper class for testing SNMP"
+
+ def __init__(self, router, iface, community, version):
+ self.community = community
+ self.version = version
+ self.router = router
+ self.iface = iface
+ logger.info(
+ "created SNMP tester: SNMPv{0} community:{1}".format(
+ self.version, self.community
+ )
+ )
+
+ def _snmp_config(self):
+ """
+ Helper function to build a string with SNMP
+ configuration for commands.
+ """
+ return "-v {0} -c {1} {2}".format(self.version, self.community, self.iface)
+
+ @staticmethod
+ def _get_snmp_value(snmp_output):
+ tokens = snmp_output.strip().split()
+
+ num_value_tokens = len(tokens) - 3
+
+ # this copes with the emptys string return
+ if num_value_tokens == 0:
+ return tokens[2]
+
+ if num_value_tokens > 1:
+ output = ""
+ index = 3
+ while index < len(tokens) - 1:
+ output += "{} ".format(tokens[index])
+ index += 1
+ output += "{}".format(tokens[index])
+ return output
+ # third token is the value of the object
+ return tokens[3]
+
+ @staticmethod
+ def _get_snmp_oid(snmp_output):
+ tokens = snmp_output.strip().split()
+
+ # third token onwards is the value of the object
+ return tokens[0].split(".", 1)[1]
+
+ @staticmethod
+ def _get_snmp_oid(snmp_output):
+ tokens = snmp_output.strip().split()
+
+# if len(tokens) > 5:
+# return None
+
+
+ # third token is the value of the object
+ return tokens[0].split('.',1)[1]
+
+ def _parse_multiline(self, snmp_output):
+ results = snmp_output.strip().split("\r\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/test_json.py b/tests/topotests/lib/test/test_json.py
index b85e193d3b..7b3c8593cc 100755
--- a/tests/topotests/lib/test/test_json.py
+++ b/tests/topotests/lib/test/test_json.py
@@ -107,16 +107,25 @@ def test_json_intersect_multilevel_true():
dcomplete = {
"i1": "item1",
"i2": "item2",
- "i3": {"i100": "item100",},
+ "i3": {
+ "i100": "item100",
+ },
"i4": {
- "i41": {"i411": "item411",},
- "i42": {"i421": "item421", "i422": "item422",},
+ "i41": {
+ "i411": "item411",
+ },
+ "i42": {
+ "i421": "item421",
+ "i422": "item422",
+ },
},
}
dsub1 = {
"i1": "item1",
- "i3": {"i100": "item100",},
+ "i3": {
+ "i100": "item100",
+ },
"i10": None,
}
dsub2 = {
@@ -126,10 +135,36 @@ def test_json_intersect_multilevel_true():
}
dsub3 = {
"i2": "item2",
- "i4": {"i41": {"i411": "item411",}, "i42": {"i422": "item422", "i450": None,}},
+ "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,
+ }
+ },
}
- 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
@@ -144,17 +179,26 @@ def test_json_intersect_multilevel_false():
dcomplete = {
"i1": "item1",
"i2": "item2",
- "i3": {"i100": "item100",},
+ "i3": {
+ "i100": "item100",
+ },
"i4": {
- "i41": {"i411": "item411",},
- "i42": {"i421": "item421", "i422": "item422",},
+ "i41": {
+ "i411": "item411",
+ },
+ "i42": {
+ "i421": "item421",
+ "i422": "item422",
+ },
},
}
# Incorrect sub-level value
dsub1 = {
"i1": "item1",
- "i3": {"i100": "item00",},
+ "i3": {
+ "i100": "item00",
+ },
"i10": None,
}
# Inexistent sub-level
@@ -166,14 +210,41 @@ def test_json_intersect_multilevel_false():
# Inexistent sub-level value
dsub3 = {
"i1": "item1",
- "i3": {"i100": None,},
+ "i3": {
+ "i100": None,
+ },
}
# Inexistent sub-sub-level value
- dsub4 = {"i4": {"i41": {"i412": "item412",}, "i42": {"i421": "item421",}}}
+ dsub4 = {
+ "i4": {
+ "i41": {
+ "i412": "item412",
+ },
+ "i42": {
+ "i421": "item421",
+ },
+ }
+ }
# Invalid sub-sub-level value
- dsub5 = {"i4": {"i41": {"i411": "item411",}, "i42": {"i421": "item420000",}}}
+ dsub5 = {
+ "i4": {
+ "i41": {
+ "i411": "item411",
+ },
+ "i42": {
+ "i421": "item420000",
+ },
+ }
+ }
# sub-sub-level should be value
- dsub6 = {"i4": {"i41": {"i411": "item411",}, "i42": "foobar",}}
+ dsub6 = {
+ "i4": {
+ "i41": {
+ "i411": "item411",
+ },
+ "i42": "foobar",
+ }
+ }
assert json_cmp(dcomplete, dsub1) is not None
assert json_cmp(dcomplete, dsub2) is not None
@@ -187,7 +258,15 @@ def test_json_with_list_sucess():
"Test successful json comparisons that have lists."
dcomplete = {
- "list": [{"i1": "item 1", "i2": "item 2",}, {"i10": "item 10",},],
+ "list": [
+ {
+ "i1": "item 1",
+ "i2": "item 2",
+ },
+ {
+ "i10": "item 10",
+ },
+ ],
"i100": "item 100",
}
@@ -197,12 +276,19 @@ def test_json_with_list_sucess():
}
# Test list correct list items
dsub2 = {
- "list": [{"i1": "item 1",},],
+ "list": [
+ {
+ "i1": "item 1",
+ },
+ ],
"i100": "item 100",
}
# Test list correct list size
dsub3 = {
- "list": [{}, {},],
+ "list": [
+ {},
+ {},
+ ],
}
assert json_cmp(dcomplete, dsub1) is None
@@ -214,7 +300,15 @@ def test_json_with_list_failure():
"Test failed json comparisons that have lists."
dcomplete = {
- "list": [{"i1": "item 1", "i2": "item 2",}, {"i10": "item 10",},],
+ "list": [
+ {
+ "i1": "item 1",
+ "i2": "item 2",
+ },
+ {
+ "i10": "item 10",
+ },
+ ],
"i100": "item 100",
}
@@ -224,12 +318,20 @@ def test_json_with_list_failure():
}
# Test list incorrect list items
dsub2 = {
- "list": [{"i1": "item 2",},],
+ "list": [
+ {
+ "i1": "item 2",
+ },
+ ],
"i100": "item 100",
}
# Test list correct list size
dsub3 = {
- "list": [{}, {}, {},],
+ "list": [
+ {},
+ {},
+ {},
+ ],
}
assert json_cmp(dcomplete, dsub1) is not None
@@ -241,20 +343,52 @@ 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",},
+ {
+ "id": 100,
+ "value": "abc",
+ },
+ {
+ "id": 200,
+ "value": "abcd",
+ },
+ {
+ "id": 300,
+ "value": "abcde",
+ },
]
- dsub1 = [{"id": 100, "value": "abc",}]
+ dsub1 = [
+ {
+ "id": 100,
+ "value": "abc",
+ }
+ ]
- dsub2 = [{"id": 100, "value": "abc",}, {"id": 200, "value": "abcd",}]
+ dsub2 = [
+ {
+ "id": 100,
+ "value": "abc",
+ },
+ {
+ "id": 200,
+ "value": "abcd",
+ },
+ ]
- dsub3 = [{"id": 300, "value": "abcde",}]
+ dsub3 = [
+ {
+ "id": 300,
+ "value": "abcde",
+ }
+ ]
dsub4 = []
- dsub5 = [{"id": 100,}]
+ dsub5 = [
+ {
+ "id": 100,
+ }
+ ]
assert json_cmp(dcomplete, dsub1) is None
assert json_cmp(dcomplete, dsub2) is None
@@ -272,13 +406,44 @@ def test_json_list_start_failure():
{"id": 300, "value": "abcde"},
]
- dsub1 = [{"id": 100, "value": "abcd",}]
+ dsub1 = [
+ {
+ "id": 100,
+ "value": "abcd",
+ }
+ ]
- dsub2 = [{"id": 100, "value": "abc",}, {"id": 200, "value": "abc",}]
+ dsub2 = [
+ {
+ "id": 100,
+ "value": "abc",
+ },
+ {
+ "id": 200,
+ "value": "abc",
+ },
+ ]
- dsub3 = [{"id": 100, "value": "abc",}, {"id": 350, "value": "abcde",}]
+ dsub3 = [
+ {
+ "id": 100,
+ "value": "abc",
+ },
+ {
+ "id": 350,
+ "value": "abcde",
+ },
+ ]
- dsub4 = [{"value": "abcx",}, {"id": 300, "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
diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py
index eaf7f90479..f958cc11d3 100644
--- a/tests/topotests/lib/topogen.py
+++ b/tests/topotests/lib/topogen.py
@@ -336,7 +336,9 @@ class Topogen(object):
for gear in self.gears.values():
errors += gear.stop()
if len(errors) > 0:
- logger.error("Errors found post shutdown - details follow: {}".format(errors))
+ logger.error(
+ "Errors found post shutdown - details follow: {}".format(errors)
+ )
self.net.stop()
@@ -552,6 +554,8 @@ class TopoRouter(TopoGear):
RD_SHARP = 14
RD_BABEL = 15
RD_PBRD = 16
+ RD_PATH = 17
+ RD_SNMP = 18
RD = {
RD_ZEBRA: "zebra",
RD_RIP: "ripd",
@@ -569,6 +573,8 @@ class TopoRouter(TopoGear):
RD_SHARP: "sharpd",
RD_BABEL: "babeld",
RD_PBRD: "pbrd",
+ RD_PATH: "pathd",
+ RD_SNMP: "snmpd",
}
def __init__(self, tgen, cls, name, **params):
@@ -653,7 +659,7 @@ class TopoRouter(TopoGear):
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_PBR.
+ TopoRouter.RD_PIM, TopoRouter.RD_PBR, TopoRouter.RD_SNMP.
"""
daemonstr = self.RD.get(daemon)
self.logger.info('loading "{}" configuration: {}'.format(daemonstr, source))
diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py
index f2fafa5e2a..fcc6c19868 100644
--- a/tests/topotests/lib/topojson.py
+++ b/tests/topotests/lib/topojson.py
@@ -43,8 +43,9 @@ from lib.common_config import (
create_vrf_cfg,
)
+from lib.pim import create_pim_config, create_igmp_config
from lib.bgp import create_router_bgp
-from lib.ospf import create_router_ospf
+from lib.ospf import create_router_ospf, create_router_ospf6
ROUTER_LIST = []
@@ -68,20 +69,18 @@ def build_topo_from_json(tgen, topo):
topo["switches"].keys(), key=lambda x: int(re_search("\d+", x).group(0))
)
- listRouters = ROUTER_LIST[:]
- listSwitches = SWITCH_LIST[:]
+ 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)
- listRouters.append(routerN)
for switchN in SWITCH_LIST:
logger.info("Topo: Add switch {}".format(switchN))
dictSwitches[switchN] = tgen.add_switch(switchN)
- listSwitches.append(switchN)
if "ipv4base" in topo:
ipv4Next = ipaddress.IPv4Address(topo["link_ip_start"]["ipv4"])
@@ -96,33 +95,25 @@ def build_topo_from_json(tgen, topo):
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]:
-
- def link_sort(x):
- if x == "lo":
- return 0
- elif "link" in x:
- return int(x.split("-link")[1])
- else:
- return int(re_search("\d+", x).group(0))
-
for destRouterLink, data in sorted(
- topo["routers"][curRouter]["links"].items(),
- key=lambda x: link_sort(x[0]),
+ topo["routers"][curRouter]["links"].iteritems()
):
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"],
- number_to_row(curRouter),
+ router_count,
number_to_column(curRouter),
topo["lo_prefix"]["v4mask"],
)
@@ -132,7 +123,7 @@ def build_topo_from_json(tgen, topo):
):
currRouter_lo_json["ipv6"] = "{}{}:{}/{}".format(
topo["lo_prefix"]["ipv6"],
- number_to_row(curRouter),
+ router_count,
number_to_column(curRouter),
topo["lo_prefix"]["v6mask"],
)
@@ -167,6 +158,14 @@ def build_topo_from_json(tgen, topo):
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
@@ -311,8 +310,11 @@ def build_config_from_json(tgen, topo, save_bkup=True):
("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),
("bgp", create_router_bgp),
("ospf", create_router_ospf),
+ ("ospf6", create_router_ospf6),
]
)
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
index 20d60ebbef..1e6ef1b2b3 100644
--- a/tests/topotests/lib/topotest.py
+++ b/tests/topotests/lib/topotest.py
@@ -609,8 +609,10 @@ def interface_set_status(node, ifacename, ifaceaction=False, vrf_name=None):
ifacename, str_ifaceaction
)
else:
- cmd = 'vtysh -c "configure terminal" -c "interface {0} vrf {1}" -c "{2}"'.format(
- ifacename, vrf_name, str_ifaceaction
+ cmd = (
+ 'vtysh -c "configure terminal" -c "interface {0} vrf {1}" -c "{2}"'.format(
+ ifacename, vrf_name, str_ifaceaction
+ )
)
node.run(cmd)
@@ -924,40 +926,44 @@ def checkAddressSanitizerError(output, router, component, logdir=""):
)
if addressSanitizerLog:
# Find Calling Test. Could be multiple steps back
- testframe=sys._current_frames().values()[0]
- level=0
+ testframe = sys._current_frames().values()[0]
+ level = 0
while level < 10:
- test=os.path.splitext(os.path.basename(testframe.f_globals["__file__"]))[0]
+ 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__"])
+ callingTest = os.path.basename(testframe.f_globals["__file__"])
break
- level=level+1
- testframe=testframe.f_back
- if (level >= 10):
+ level = level + 1
+ testframe = testframe.f_back
+ if level >= 10:
# somehow couldn't find the test script.
- callingTest="unknownTest"
+ callingTest = "unknownTest"
#
# Now finding Calling Procedure
- level=0
+ 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_internal") and
- (callingProc != "stop") and
- (callingProc != "stop_topology") and
- (callingProc != "checkRouterRunning") and
- (callingProc != "check_router_running") and
- (callingProc != "routers_have_failure")):
+ callingProc = sys._getframe(level).f_code.co_name
+ if (
+ (callingProc != "processAddressSanitizerError")
+ and (callingProc != "checkAddressSanitizerError")
+ and (callingProc != "checkRouterCores")
+ and (callingProc != "stopRouter")
+ and (callingProc != "__stop_internal")
+ 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):
+ level = level + 1
+ if level >= 20:
# something wrong - couldn't found the calling test function
- callingProc="unknownProc"
+ callingProc = "unknownProc"
with open("/tmp/AddressSanitzer.txt", "a") as addrSanFile:
sys.stderr.write(
"AddressSanitizer error in topotest `%s`, test `%s`, router `%s`\n\n"
@@ -979,7 +985,6 @@ def checkAddressSanitizerError(output, router, component, logdir=""):
addrSanFile.write("\n---------------\n")
return
-
addressSanitizerError = re.search(
"(==[0-9]+==)ERROR: AddressSanitizer: ([^\s]*) ", output
)
@@ -989,16 +994,20 @@ def checkAddressSanitizerError(output, router, component, logdir=""):
# No Address Sanitizer Error in Output. Now check for AddressSanitizer daemon file
if logdir:
- filepattern=logdir+"/"+router+"/"+component+".asan.*"
- logger.debug("Log check for %s on %s, pattern %s\n" % (component, router, filepattern))
+ filepattern = logdir + "/" + router + "/" + component + ".asan.*"
+ logger.debug(
+ "Log check for %s on %s, pattern %s\n" % (component, router, filepattern)
+ )
for file in glob.glob(filepattern):
with open(file, "r") as asanErrorFile:
- asanError=asanErrorFile.read()
+ asanError = asanErrorFile.read()
addressSanitizerError = re.search(
"(==[0-9]+==)ERROR: AddressSanitizer: ([^\s]*) ", asanError
- )
+ )
if addressSanitizerError:
- processAddressSanitizerError(addressSanitizerError, asanError, router, component)
+ processAddressSanitizerError(
+ addressSanitizerError, asanError, router, component
+ )
return True
return False
@@ -1065,7 +1074,7 @@ class Router(Node):
if self.logdir is None:
cur_test = os.environ["PYTEST_CURRENT_TEST"]
self.logdir = "/tmp/topotests/" + cur_test[
- cur_test.find("/")+1 : cur_test.find(".py")
+ cur_test.find("/") + 1 : cur_test.find(".py")
].replace("/", ".")
# If the logdir is not created, then create it and set the
@@ -1073,7 +1082,7 @@ class Router(Node):
if not os.path.isdir(self.logdir):
os.system("mkdir -p " + self.logdir + "/" + name)
os.system("chmod -R go+rw /tmp/topotests")
- # Erase logs of previous run
+ # Erase logs of previous run
os.system("rm -rf " + self.logdir + "/" + name)
self.daemondir = None
@@ -1096,6 +1105,8 @@ class Router(Node):
"sharpd": 0,
"babeld": 0,
"pbrd": 0,
+ "pathd": 0,
+ "snmpd": 0,
}
self.daemons_options = {"zebra": ""}
self.reportCores = True
@@ -1279,6 +1290,8 @@ class Router(Node):
% (self.routertype, self.routertype, self.routertype, daemon)
)
self.waitOutput()
+ if (daemon == "snmpd") and (self.routertype == "frr"):
+ self.cmd('echo "agentXSocket /etc/frr/agentx" > /etc/snmp/frr.conf')
if (daemon == "zebra") and (self.daemons["staticd"] == 0):
# Add staticd with zebra - if it exists
staticd_path = os.path.join(self.daemondir, "staticd")
@@ -1435,6 +1448,20 @@ class Router(Node):
while "staticd" in daemons_list:
daemons_list.remove("staticd")
+ if "snmpd" in daemons_list:
+ snmpd_path = "/usr/sbin/snmpd"
+ snmpd_option = self.daemons_options["snmpd"]
+ self.cmd(
+ "{0} {1} -C -c /etc/frr/snmpd.conf -p /var/run/{2}/snmpd.pid -x /etc/frr/agentx > snmpd.out 2> snmpd.err".format(
+ snmpd_path, snmpd_option, self.routertype
+ )
+ )
+ logger.info("{}: {} snmpd started".format(self, self.routertype))
+
+ # Remove `snmpd` so we don't attempt to start it again.
+ while "snmpd" in daemons_list:
+ daemons_list.remove("snmpd")
+
# Fix Link-Local Addresses
# Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this
self.cmd(
@@ -1598,6 +1625,8 @@ class Router(Node):
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":
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 0000000000..14cb0bee1d
--- /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 0000000000..c670c82d21
--- /dev/null
+++ b/tests/topotests/multicast-pim-bsm-topo1/test_mcast_pim_bsmp_01.py
@@ -0,0 +1,1653 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Following tests are covered to test PIM BSM processing basic functionality:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Tests covered in this suite
+1. Verify FRR router select higher IP BSR , when 2 BSR present in the network
+2. Verify BSR and RP updated correctly after configuring as black hole address
+3.1 Verify when new router added to the topology, FRR node will send
+ unicast BSM to new router
+3.2 Verify if no forwarding bit is set , FRR is not forwarding the
+ BSM to other PIM nbrs
+3.3 Verify multicast BSM is sent to new router when unicast BSM is disabled
+4.1 Verfiy 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 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 mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ addKernelRoute,
+ create_static_routes,
+ iperfSendIGMPJoin,
+ stop_router,
+ start_router,
+ shutdown_bringup_interface,
+ kill_router_daemons,
+ start_router_daemons,
+ reset_config_on_routers,
+ do_countdown,
+ apply_raw_config,
+ kill_iperf,
+ run_frr_cmd,
+ required_linux_kernel_version,
+ topo_daemons,
+)
+
+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_ip_mroutes,
+ verify_join_state_and_timer,
+ verify_pim_state,
+ verify_upstream_iif,
+ verify_igmp_groups,
+ verify_ip_pim_upstream_rpf,
+ enable_disable_pim_unicast_bsm,
+ enable_disable_pim_bsm,
+ clear_ip_mroute,
+ clear_ip_pim_interface_traffic,
+ verify_pim_interface_traffic,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/mcast_pim_bsmp_01.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+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"
+
+
+class CreateTopo(Topo):
+ """
+ Test BasicTopo - topology 1
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+ logger.info("Master Topology: \n {}".format(TOPOLOGY))
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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 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 = {
+ fhr: {"static_routes": [{"network": bsr_route, "next_hop": next_hop}]},
+ 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)
+
+ # Add kernal 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 kernal 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 kernal 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 kernal 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)
+
+ 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)
+ 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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # 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("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)
+
+ # 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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 is become prefered 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 is become prefered 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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # 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("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]
+
+ input_dict = {
+ "f1": {"static_routes": [{"network": BSR1_ADDR, "next_hop": NEXT_HOP1}]},
+ "i1": {"static_routes": [{"network": BSR1_ADDR, "next_hop": next_hop_rp}]},
+ "l1": {"static_routes": [{"network": BSR1_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)
+
+ # Use scapy to send pre-defined packet from senser to receiver
+
+ group = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet9"]["group"]
+ CRP = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet9"]["candidate_rp"]
+ 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)
+
+ 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 = 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("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 Error {}".format(tc_name, result)
+
+ state_after = verify_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}
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, 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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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 Error {}".format(tc_name, result)
+
+ # unconfigure unicast bsm on f1-i1-eth2
+ step("unconfigure unicast bsm on f1-i1-eth2, will forward with only mcast")
+ enable_disable_pim_unicast_bsm(tgen, "f1", "f1-i1-eth2", enable=False)
+
+ # Reboot i1 to check if still bsm received with multicast address
+ step("Reboot i1 to check if still bsm received with multicast address")
+ stop_router(tgen, "i1")
+ start_router(tgen, "i1")
+
+ result = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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)
+
+ # Verify ip mroute populated again
+ step("Verify mroute again on l1 (lhr)")
+ result = verify_ip_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. Verfiy 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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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_ip_mroutes(
+ tgen, "i1", src_addr, GROUP_ADDRESS, iif, oil, expected=False
+ )
+ assert result is not True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # unconfigure bsm processing on f1 on f1-i1-eth2
+ step("unconfigure bsm processing on f1 in f1-i1-eth2, will drop bsm")
+ result = enable_disable_pim_bsm(tgen, "f1", "f1-i1-eth2", enable=False)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ step("Send BSM packet from b1")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify bsr state in FHR
+ step("Verify if b1 chosen as BSR in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify bsr state in i1
+ step("Verify if b1 is not chosen as BSR in i1")
+ result = verify_pim_bsr(tgen, topo, "i1", bsr_ip, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # check if mroute still not installed because of rp not available
+ step("check if mroute still not installed because of rp not available")
+ result = verify_ip_mroutes(
+ tgen, "i1", src_addr, GROUP_ADDRESS, iif, oil, expected=False
+ )
+ assert result is not True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # configure bsm processing on i1 on f1-i1-eth2
+ step("configure bsm processing on f1 in f1-i1-eth2, will accept bsm")
+ result = enable_disable_pim_bsm(tgen, "f1", "f1-i1-eth2", enable=True)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ step("Send BSM packet again from b1")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet2")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify again if BSR is installed from bsm forwarded by f1
+ step("Verify again if BSR is installed from bsm forwarded by f1")
+ result = verify_pim_bsr(tgen, topo, "i1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # verify ip mroute populated
+ step("Verify ip mroute")
+ result = verify_ip_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_ip_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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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_ip_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_ip_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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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,
+ )
+
+ # 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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"
+
+ 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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 Error {}".format(tc_name, result)
+
+ # Verify RP mapping removed after hold timer expires
+ group = "225.1.1.1/32"
+ step("Verify RP mapping removed after hold timer expires in l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp == {}, "Testcase {} :Failed \n Error : RP found when not expected".format(
+ tc_name
+ )
+
+ # Verify iif is unknown after RP timeout
+ step("Verify iif is unknown after RP timeout in l1")
+ iif = "Unknown"
+ result = verify_upstream_iif(
+ tgen, dut, iif, src_addr, GROUP_ADDRESS, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify join state and join timer
+ step("Verify join state and join timer in l1")
+ iif = "l1-i1-eth0"
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, src_addr, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify ip mroute is not installed
+ step("Verify mroute not installed in l1")
+ result = verify_ip_mroutes(
+ tgen, dut, src_addr, GROUP_ADDRESS, iif, oil, expected=False
+ )
+ assert result is not 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_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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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)
+
+ # 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_ip_mroutes(
+ tgen, dut, src_addr, GROUP_ADDRESS, iif, oil, expected=False
+ )
+ assert result is not True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Add back route for RP to make it reachable
+ step("Add back route for RP to make it reachable")
+ input_dict = {
+ "l1": {"static_routes": [{"network": rp_ip, "next_hop": next_hop_lhr,}]}
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify that (*,G) installed in mroute again
+ iif = "l1-i1-eth0"
+ result = verify_ip_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 0000000000..14cb0bee1d
--- /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 0000000000..459afb5a02
--- /dev/null
+++ b/tests/topotests/multicast-pim-bsm-topo2/test_mcast_pim_bsmp_02.py
@@ -0,0 +1,1115 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Following tests are covered to test PIM BSM processing basic functionality:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Tests covered in this suite
+1. Verify (*,G) mroute detail on FRR router after BSM rp installed
+2. Verify group to RP updated correctly on FRR router, when BSR advertising
+ the overlapping group address
+3. Verify group to RP info is updated correctly, when BSR advertising the
+ same RP with different priority
+4. Verify group to RP mapping in FRR node when 2 BSR are present in the network
+ and both are having same BSR priority
+5. Verify RP is selected based on hash function, when BSR advertising the group
+ to RP mapping with same priority
+6. Verify fragmentation of bootstrap message
+7. Verify when candidate RP advertised with 32 mask length
+ and contain all the contacts
+"""
+
+import os
+import sys
+import 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 mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ addKernelRoute,
+ create_static_routes,
+ iperfSendIGMPJoin,
+ stop_router,
+ start_router,
+ shutdown_bringup_interface,
+ kill_router_daemons,
+ start_router_daemons,
+ reset_config_on_routers,
+ do_countdown,
+ apply_raw_config,
+ kill_iperf,
+ run_frr_cmd,
+ required_linux_kernel_version,
+ topo_daemons,
+)
+
+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_ip_mroutes,
+ verify_join_state_and_timer,
+ verify_pim_state,
+ verify_upstream_iif,
+ verify_igmp_groups,
+ verify_ip_pim_upstream_rpf,
+ enable_disable_pim_unicast_bsm,
+ enable_disable_pim_bsm,
+ clear_ip_mroute,
+ clear_ip_pim_interface_traffic,
+ verify_pim_interface_traffic,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/mcast_pim_bsmp_02.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+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"
+
+
+class CreateTopo(Topo):
+ """
+ Test BasicTopo - topology 1
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+ logger.info("Master Topology: \n {}".format(TOPOLOGY))
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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 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 = {
+ fhr: {"static_routes": [{"network": bsr_route, "next_hop": next_hop}]},
+ 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)
+
+ # Add kernal 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 kernal 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 kernal 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 kernal 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)
+
+ 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)
+ 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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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_ip_mroutes(
+ tgen, dut, src_addr, GROUP_ADDRESS, iif, oil, wait=20, expected=False
+ )
+ assert result is not True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Send BSM again to configure rp
+ step("Add back RP by sending BSM from b1")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify that (*,G) installed in mroute again
+ iif = "l1-i1-eth0"
+ result = verify_ip_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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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_ip_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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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,
+ )
+
+ # 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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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 Error {}".format(tc_name, result)
+
+ # Verify if b1 is still chosen as bsr
+ step("Verify if b1 is still chosen as bsr in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify if that rp is installed
+ step("Verify that same RP in istalled in LHR l1")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+def test_RP_hash_p0(request):
+ """
+ Verify RP is selected based on hash function, when BSR advertising the group
+ to RP mapping with same priority
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ reset_config_on_routers(tgen)
+
+ # 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"
+
+ 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("ifconfig f1-i1-eth2 mtu 100")
+ inter_node.run("ifconfig 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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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)
+
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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("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 = iperfSendIGMPJoin(tgen, "r1", GROUP_ADDRESS, join_interval=1)
+ 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-sm-topo1/multicast_pim_sm_topo1.json b/tests/topotests/multicast-pim-sm-topo1/multicast_pim_sm_topo1.json
new file mode 100644
index 0000000000..71454c2ab2
--- /dev/null
+++ b/tests/topotests/multicast-pim-sm-topo1/multicast_pim_sm_topo1.json
@@ -0,0 +1,140 @@
+{
+ "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"}
+ },
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1" :{
+ "igmp":{
+ "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"}
+ },
+ "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"}
+ },
+ "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"}
+ },
+ "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"}
+ },
+ "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 0000000000..ac675c5c2f
--- /dev/null
+++ b/tests/topotests/multicast-pim-sm-topo1/test_multicast_pim_sm_topo1.py
@@ -0,0 +1,1698 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Following tests are covered to test multicast pim sm:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+1. TC_1_1: Verify Multicast data traffic with static RP, (*,g) and
+ (s,g) OIL updated correctly
+2. TC_1_2: Verify Multicast data traffic with static RP, (*,g) and
+ (s,g) OIL updated correctly
+3. TC_4: Verify removing the RP should not impact the multicast
+ data traffic
+4. TC_5: Verify (*,G) and (S,G) entry populated again after clear the
+ PIM nbr and mroute from FRR node
+5. TC_9: Verify (s,g) timeout from FHR and RP when same receive
+ exist in LHR , FHR and RP
+6. TC_19: Verify mroute detail when same receiver joining 5
+ different sources
+7. TC_16: Verify (*,G) and (S,G) populated correctly
+ when FRR is the transit router
+8. TC_23: Verify (S,G) should not create if RP is not reachable
+9. TC_24: Verify modification of IGMP query timer should get update
+ accordingly
+10. TC_25: Verify modification of IGMP max query response timer
+ should get update accordingly
+"""
+
+import os
+import sys
+import json
+import time
+import datetime
+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 mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ iperfSendIGMPJoin,
+ addKernelRoute,
+ reset_config_on_routers,
+ iperfSendTraffic,
+ kill_iperf,
+ 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_ip_mroutes,
+ verify_pim_interface_traffic,
+ verify_upstream_iif,
+ verify_pim_neighbors,
+ verify_pim_state,
+ verify_ip_pim_join,
+ clear_ip_mroute,
+ clear_ip_pim_interface_traffic,
+ verify_igmp_config,
+ clear_ip_mroute_verify
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/multicast_pim_sm_topo1.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+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"]
+
+
+class CreateTopo(Topo):
+ """
+ Test BasicTopo - topology 1
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # 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")
+
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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 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
+
+
+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()
+ 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)")
+ intf_i1_l1 = topo["routers"]["i1"]["links"]["l1"]["interface"]
+ result = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i1", intf_i1_l1, GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("joinRx value before join sent")
+ intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"]
+ state_dict = {"r2": {intf_r2_l1: ["joinRx"]}}
+ 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)
+
+ result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN, join_interval=1)
+ 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")
+ intf_i2_f1 = topo["routers"]["i2"]["links"]["f1"]["interface"]
+ result = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i2", intf_i2_f1, GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, "i2", IGMP_JOIN, 32, 2500)
+ 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_ip_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 = verify_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_ip_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()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, "i2", IGMP_JOIN, 32, 2500)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i1", "i1-l1-eth0", GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("joinRx value before join sent")
+ state_dict = {"r2": {"r2-l1-eth2": ["joinRx"]}}
+ 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)
+
+ result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN, join_interval=1)
+ 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"},
+ ]
+ for data in input_dict:
+ result = verify_ip_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 = verify_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_ip_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()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i1", "i1-l1-eth0", GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN_RANGE_1, join_interval=1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3, wait for SPT switchover")
+ result = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, "i2", IGMP_JOIN_RANGE_1, 32, 2500)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Clear the mroute on l1, wait for 5 sec")
+ result = clear_ip_mroute_verify(tgen, "l1")
+ 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 ip mroute' and "
+ " '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"}
+ ]
+
+ for data in input_dict:
+ result = verify_ip_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()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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"}}}}},
+ "r2": {"igmp": {"interfaces": {"r2-i3-eth1": {"igmp": {"version": "2"}}}}},
+ }
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN, join_interval=1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from R3 to 225.1.1.1 receiver")
+ result = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, "i2", IGMP_JOIN, 32, 2500)
+ 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)
+ 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_ip_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()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i1", "i1-l1-eth0", _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, "i1", _IGMP_JOIN_RANGE, join_interval=1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "f1": {"igmp": {"interfaces": {"f1-i8-eth2": {"igmp": {"version": "2"}}}}}
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i8", "i8-f1-eth0", _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, "i8", _IGMP_JOIN_RANGE, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500)
+ 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_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n mroutes are"
+ " still present \n Error: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step(
+ "Source which is stopped got removed , other source"
+ " after still present verify using 'show ip mroute' "
+ )
+ input_dict = [
+ {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"},
+ {
+ "dut": "l1",
+ "src_address": source_i5,
+ "iif": "l1-c1-eth0",
+ "oil": "l1-i1-eth1",
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i3,
+ "iif": "l1-r2-eth4",
+ "oil": "l1-i1-eth1",
+ },
+ {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"},
+ {
+ "dut": "f1",
+ "src_address": source_i5,
+ "iif": "f1-c2-eth0",
+ "oil": "f1-i8-eth2",
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i3,
+ "iif": "f1-r2-eth3",
+ "oil": "f1-i8-eth2",
+ },
+ ]
+ for data in input_dict:
+ result = verify_ip_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_ip_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()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i1", "i1-l1-eth0", GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN_RANGE_1, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, "i2", IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_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_ip_mroutes(
+ tgen, "c1", "*", IGMP_JOIN, "c1-c2-eth1", "c1-l1-eth0", expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \n mroutes are"
+ " still present \n Error: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_when_RP_unreachable_p1(request):
+ """
+ TC_23: Verify (S,G) should not create if RP is not reachable
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i1", "i1-l1-eth0", GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN, join_interval=1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to 225.1.1.1 receiver")
+ result = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, "i2", IGMP_JOIN, 32, 2500)
+ 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"}}}}}
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i8", "i8-f1-eth0", GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, "i8", IGMP_JOIN, join_interval=1)
+ 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_ip_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_ip_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_ip_mroutes(
+ tgen, "f1", "*", IGMP_JOIN, "f1-r2-eth3", "f1-i8-eth2", expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \n mroutes are"
+ " still present \n Error: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("IGMP groups are present verify using 'show ip igmp group'")
+ dut = "l1"
+ interface = "l1-i1-eth1"
+ result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_modify_igmp_query_timer_p0(request):
+ """
+ TC_24:
+ Verify modification of IGMP query timer should get update
+ accordingly
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Enable IGMP on FRR1 interface and send IGMP join (225.1.1.1)")
+ result = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i1", "i1-l1-eth0", GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN, join_interval=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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, "i2", IGMP_JOIN, 32, 2500)
+ 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_ip_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": 100}}}
+ }
+ }
+ }
+ }
+ 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": 200}}}
+ }
+ }
+ }
+ }
+ 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": 300}}}
+ }
+ }
+ }
+ }
+ 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()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Enable IGMP on FRR1 interface and send IGMP join (225.1.1.1)")
+ result = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i1", "i1-l1-eth0", GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, "i1", IGMP_JOIN, join_interval=1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure IGMP query response time to 10 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i2", "i2-f1-eth0", GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, "i2", IGMP_JOIN, 32, 2500)
+ 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_ip_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")
+ input_dict_1 = {"l1": {"pim": {"disable": ["l1-i1-eth1"]}}}
+ result = create_pim_config(tgen, topo, input_dict_1)
+ 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 100sec 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": 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": 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)
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "version": "2",
+ "query": {"query-max-response-time": 140},
+ }
+ }
+ }
+ }
+ }
+ }
+ 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": 150},
+ }
+ }
+ }
+ }
+ }
+ }
+ 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 0000000000..71454c2ab2
--- /dev/null
+++ b/tests/topotests/multicast-pim-sm-topo2/multicast_pim_sm_topo2.json
@@ -0,0 +1,140 @@
+{
+ "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"}
+ },
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1" :{
+ "igmp":{
+ "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"}
+ },
+ "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"}
+ },
+ "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"}
+ },
+ "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"}
+ },
+ "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 0000000000..a9d914da57
--- /dev/null
+++ b/tests/topotests/multicast-pim-sm-topo2/test_multicast_pim_sm_topo2.py
@@ -0,0 +1,1947 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Following tests are covered to test multicast pim sm:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+1. TC_17: Verify (*,G) and (S,G) present and multicast traffic resume,
+ after restart of PIMd daemon
+2. TC_18: Verify (*,G) and (S,G) present and multicast traffic resume after
+ FRR service stop and start
+3. TC_10: Verify SPT switchover working when RPT and SPT path is
+ different
+4. TC_15: Verify (S,G) and (*,G) mroute after shut / no shut of upstream
+ interfaces
+5. TC_7: Verify mroute detail when receiver is present
+ outside of FRR
+6. TC_8: Verify mroute when FRR is acting as FHR and LHR
+7. TC_20: Verify mroute detail when 5 different receiver joining
+ same source
+8. TC_22: Verify OIL and IIF detail updated in (S,G) mroute after shut
+ and no shut of the source interface
+"""
+
+import os
+import sys
+import json
+import time
+import datetime
+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 mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ iperfSendIGMPJoin,
+ addKernelRoute,
+ reset_config_on_routers,
+ iperfSendTraffic,
+ kill_iperf,
+ 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_ip_mroutes,
+ verify_pim_interface_traffic,
+ verify_upstream_iif,
+ verify_pim_neighbors,
+ verify_pim_state,
+ verify_ip_pim_join,
+ clear_ip_mroute,
+ clear_ip_pim_interface_traffic,
+ verify_igmp_config,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/multicast_pim_sm_topo2.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+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"]
+
+
+class CreateTopo(Topo):
+ """
+ Test BasicTopo - topology 1
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # 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")
+
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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 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
+
+
+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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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"}}}}}
+ }
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500)
+ 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_ip_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_ip_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_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n mroutes are"
+ " still present \n Error: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_and_traffic_when_frr_restarted_p2(request):
+ """
+ TC_18: Verify (*,G) and (S,G) present and multicast traffic resume after
+ FRR service stop and start
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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"}}}}}
+ }
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500)
+ 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_ip_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_ip_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_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n mroutes are"
+ " still present \n Error: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_verify_SPT_switchover_when_RPT_and_SPT_path_is_different_p0(request):
+ """
+ TC_10: Verify SPT switchover working when RPT and SPT path is
+ different
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i1", "i1-l1-eth0", _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, "i1", _IGMP_JOIN_RANGE, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i2", "i2-f1-eth0", _GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("registerRx and registerStopTx value before traffic sent")
+ state_dict = {"c2": {"c2-f1-eth1": ["registerRx", "registerStopTx"]}}
+ 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)
+
+ result = iperfSendTraffic(tgen, "i2", _IGMP_JOIN_RANGE, 32, 2500)
+ 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_ip_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_ip_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_ip_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")
+
+ kill_iperf(tgen, "i2", "remove_traffic")
+
+ 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_ip_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 = verify_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)
+
+ 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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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"}}}}}
+ }
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500)
+ 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_ip_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_ip_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_ip_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_ip_mroute(tgen, "l1")
+ clear_ip_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_ip_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)
+
+ done_flag = False
+ for retry in range(1, 11):
+ result = verify_upstream_iif(tgen, "l1", "Unknown", source, IGMP_JOIN_RANGE_2,
+ expected=False)
+ if result is not True:
+ done_flag = True
+ else:
+ continue
+
+ if done_flag:
+ logger.info("Expected Behavior: {}".format(result))
+ break
+
+ assert done_flag is True, (
+ "Testcase {} : Failed Error: \n "
+ "mroutes are still present, after waiting for 10 mins".format(tc_name)
+ )
+
+ step("No shut the Source interface just after the upstream is expired" " from FRR1")
+ shutdown_bringup_interface(tgen, dut, intf_l1_r2, True)
+ shutdown_bringup_interface(tgen, dut, intf_l1_c1, True)
+
+ for data in input_dict:
+ result = verify_ip_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")
+ kill_iperf(tgen)
+
+ for data in input_dict:
+ result = verify_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n mroutes are "
+ "still present \n Error: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_when_receiver_is_outside_frr_p0(request):
+ """
+ TC_7: Verify mroute detail when receiver is present
+ outside of FRR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i1", "i1-l1-eth0", _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, "i1", _IGMP_JOIN_RANGE, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i2", "i2-f1-eth0", _GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, "i2", _IGMP_JOIN_RANGE, 32, 2500)
+ 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"}}}}}
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i5", "i5-c2-eth0", _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, "i5", _IGMP_JOIN_RANGE, join_interval=1)
+ 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_ip_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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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)"
+ )
+
+ result = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i1", "i1-l1-eth0", _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i2", "i2-f1-eth0", _GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP join (226.1.1.1-5, 232.1.1.1-5) to LHR(l1)")
+ result = iperfSendIGMPJoin(tgen, "i1", _IGMP_JOIN_RANGE, join_interval=1)
+ 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 = iperfSendTraffic(tgen, "i2", _IGMP_JOIN_RANGE, 32, 2500)
+ 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"}}}}}
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, "i8", "i8-f1-eth0", _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, "i8", _IGMP_JOIN_RANGE, join_interval=1)
+ 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_ip_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")
+ kill_iperf(tgen)
+
+ 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"},
+ ]
+
+ done_flag = False
+ for retry in range(1, 11):
+ for data in input_dict:
+ result = verify_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+
+ if result is True:
+ done_flag = True
+ else:
+ continue
+
+ if done_flag:
+ break
+
+ assert done_flag is True, (
+ "Testcase {} : Failed Error: \n "
+ "mroutes are still present, after waiting for 10 mins".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"},
+ ]
+
+ done_flag = False
+ for retry in range(1, 11):
+ for data in input_dict:
+ result = verify_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False
+ )
+ if result is not True:
+ done_flag = True
+ else:
+ continue
+
+ if done_flag:
+ logger.info("Expected Behavior: {}".format(result))
+ break
+
+ assert done_flag is True, (
+ "Testcase {} : Failed Error: \n "
+ "mroutes are still present, after waiting for 10 mins".format(tc_name)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_when_5_different_receiver_joining_same_sources_p0(request):
+ """
+ TC_20: Verify mroute detail when 5 different receiver joining
+ same source
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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"}},
+ "f1-i2-eth1": {"igmp": {"version": "2"}},
+ }
+ }
+ },
+ "l1": {"igmp": {"interfaces": {"l1-i6-eth2": {"igmp": {"version": "2"}}}}},
+ }
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500)
+ 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", "oil": "l1-i1-eth1"},
+ {"dut": "f1", "src_address": source, "iif": "f1-r2-eth3", "oil": "f1-i8-eth2"},
+ ]
+ for data in input_dict_all:
+ result = verify_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n mroutes are"
+ " still present \n Error: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step(
+ "No traffic impact observed on other receivers verify using"
+ " 'show ip mroute' "
+ )
+ input_dict = [
+ {"dut": "f1", "src_address": source, "iif": "f1-r2-eth3", "oil": "f1-i8-eth2"}
+ ]
+ for data in input_dict:
+ result = verify_ip_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_ip_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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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 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"}}}}}
+ }
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500)
+ 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_ip_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_ip_mroutes(
+ tgen,
+ "f1",
+ source_i2,
+ _IGMP_JOIN_RANGE,
+ "f1-i2-eth1",
+ "f1-i8-eth2",
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n mroutes are"
+ " still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ result = verify_upstream_iif(
+ tgen, "f1", "Unknown", "10.0.5.2", _IGMP_JOIN_RANGE, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo3.json b/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo3.json
new file mode 100644
index 0000000000..f582f4929d
--- /dev/null
+++ b/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo3.json
@@ -0,0 +1,140 @@
+{
+ "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"}
+ },
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1" :{
+ "igmp":{
+ "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"}
+ },
+ "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"}
+ },
+ "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"}
+ },
+ "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"}
+ },
+ "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 0000000000..4635dac7d2
--- /dev/null
+++ b/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo4.json
@@ -0,0 +1,137 @@
+{
+ "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"}
+ },
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1" :{
+ "igmp":{
+ "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"}
+ },
+ "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"}
+ },
+ "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"}
+ },
+ "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"}
+ },
+ "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 0000000000..fdceb77fd1
--- /dev/null
+++ b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo3.py
@@ -0,0 +1,4609 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Following tests are covered to test multicast pim sm:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+
+1. verify oil when join prune sent scenario_1 p0
+2. verify oil when join prune sent scenario_2 p0
+3. shut noshut source interface when upstream cleared from LHR p0(
+4. shut noshut receiver interface when upstream cleared from LHR p0(
+5. verify igmp clis p0
+6. verify igmp cli generate query once p0
+7. verify remove add igmp config to receiver interface p0
+8. verify remove add igmp commands when pim configured p0
+9. verify remove add pim commands when igmp configured p0
+10. pim dr priority p0
+11. pim hello timer p0
+12. Verify mroute after removing RP sending IGMP prune p2
+13. Verify prune is sent to LHR and FHR when PIM nbr went down
+14. Verify mroute flag in LHR and FHR node
+15. Verify IGMP prune processed correctly when same join received from IGMP and PIM
+16. Verify multicast traffic flowing fine, when LHR connected to RP
+17. Verify multicast traffic is flowing fine when FHR is connected to RP
+"""
+
+import os
+import re
+import sys
+import json
+import time
+import datetime
+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 mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ iperfSendIGMPJoin,
+ addKernelRoute,
+ reset_config_on_routers,
+ iperfSendTraffic,
+ kill_iperf,
+ shutdown_bringup_interface,
+ kill_router_daemons,
+ start_router,
+ start_router_daemons,
+ stop_router,
+ apply_raw_config,
+ add_interfaces_to_vlan,
+ tcpdump_capture_start,
+ tcpdump_capture_stop,
+ LOGDIR,
+ check_router_status,
+ required_linux_kernel_version,
+ topo_daemons,
+)
+from lib.pim import (
+ create_pim_config,
+ create_igmp_config,
+ verify_igmp_groups,
+ verify_ip_mroutes,
+ clear_ip_mroute_verify,
+ clear_ip_mroute,
+ clear_ip_pim_interface_traffic,
+ verify_igmp_config,
+ verify_pim_neighbors,
+ verify_pim_config,
+ verify_pim_interface,
+ verify_upstream_iif,
+ verify_multicast_traffic,
+ verify_pim_rp_info,
+ get_refCount_for_mroute,
+ verify_multicast_flag_state,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/multicast_pim_sm_topo3.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+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"}
+TCPDUMP_FILE = "{}/{}".format(LOGDIR, "v2query.txt")
+
+
+class CreateTopo(Topo):
+ """
+ Test BasicTopo - topology 1
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # 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")
+
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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 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")
+
+ for router in topo["routers"].keys():
+ if "static_routes" in topo["routers"][router]:
+ static_routes = topo["routers"][router]["static_routes"]
+ for static_route in static_routes:
+ network = static_route["network"]
+ next_hop = static_route["next_hop"]
+ if type(network) is not list:
+ network = [network]
+ for net in network:
+ addKernelRoute(tgen, router, iperf_intf, net, next_hop)
+ return True
+
+
+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 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(LOGDIR, tgen.modname, 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(LOGDIR, tgen.modname, 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 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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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"}}}}}
+ }
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " "FRR1 node")
+
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False)
+
+ step(
+ "After receiving the IGMP prune from FRR1 , verify traffic "
+ "immediately stopped for this receiver 'show ip multicast'"
+ )
+
+ input_traffic = {"l1": {"traffic_sent": [intf_l1_i1]}}
+ result = verify_multicast_traffic(tgen, input_traffic, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ " Traffic is not stopped yet \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step(
+ "IGMP groups are remove from FRR1 node 'show ip igmp groups'"
+ " FRR3 IGMP still present"
+ )
+
+ dut = "l1"
+ result = verify_igmp_groups(
+ tgen, dut, intf_l1_i1, IGMP_JOIN_RANGE_1, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "IGMP groups are not deleted \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ dut = "f1"
+ result = verify_igmp_groups(tgen, dut, intf_f1_i8, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "(*,G) and (S,G) OIL got removed immediately after receiving"
+ " prune 'show ip pim state' and 'show ip mroute' on FRR1 node,"
+ " no impact on FRR3 receiver"
+ )
+
+ input_dict_l1 = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_l1:
+ result = verify_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "mroutes are still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ for data in input_dict_l1:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "upstream entries are still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ input_dict_f1 = [
+ {
+ "dut": "f1",
+ "src_address": "*",
+ "iif": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_f1:
+ result = verify_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_f1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " " FRR3 node")
+
+ intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"]
+ shutdown_bringup_interface(tgen, "f1", intf_f1_i8, False)
+
+ step(
+ "After receiving the IGMP prune from FRR3s , verify traffic "
+ "immediately stopped for this receiver 'show ip multicast'"
+ )
+
+ input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}}
+ result = verify_multicast_traffic(tgen, input_traffic, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ " Traffic is not stopped yet \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step(
+ "IGMP groups are remove from FRR1 node 'show ip igmp groups'"
+ " FRR3 IGMP still present"
+ )
+
+ dut = "f1"
+ result = verify_igmp_groups(
+ tgen, dut, intf_f1_i8, IGMP_JOIN_RANGE_1, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "IGMP groups are not deleted \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step(
+ "(*,G) and (S,G) OIL got prune state (none) from all the nodes"
+ "FRR1, FRR3 verify using 'show ip mroute'"
+ )
+
+ input_dict_l1 = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_l1:
+ result = verify_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "mroutes are still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ for data in input_dict_l1:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "upstream entries are still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ shutdown_bringup_interface(tgen, "f1", intf_f1_i8, True)
+ shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True)
+
+ for data in input_dict_l1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_oil_when_join_prune_sent_scenario_2_p1(request):
+ """
+ TC_21_2: Verify OIL detail updated in (S,G) and (*,G) mroute when IGMP
+ join/prune is sent
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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"}}}}}
+ }
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ 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_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "mroutes are still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ input_dict_l1_r2 = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_l1_r2:
+ result = verify_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "mroutes are still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("After prune is sent verify upstream got removed in FRR1 node")
+
+ for data in input_dict_l1:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "upstream entries are still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_shut_noshut_source_interface_when_upstream_cleared_from_LHR_p1(request):
+ """
+ TC_26: Verify shut/no shut of source interface after upstream got cleared
+ from LHR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=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_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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_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_ip_mroutes(
+ tgen, "f1", source_i2, IGMP_JOIN_RANGE_1, intf_f1_i2, intf_f1_r2, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n mroutes are"
+ " still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ step(
+ "After waiting for (S,G) timeout from FRR1 for same"
+ " source verify that (S,G) is flushed from FRR1 node"
+ " 'show ip pim upstream' 'show ip mroute' "
+ )
+
+ done_flag = False
+ for retry in range(1, 11):
+ result = verify_upstream_iif(
+ tgen, "l1", "Unknown", source_i2, IGMP_JOIN_RANGE_1, expected=False
+ )
+ if result is not True:
+ done_flag = True
+ else:
+ continue
+ if done_flag:
+ logger.info("Expected Behavior: {}".format(result))
+ break
+
+ assert done_flag is True, (
+ "Testcase {} : Failed Error: \n "
+ "mroutes are still present, after waiting for 10 mins".format(tc_name)
+ )
+
+ step("No shut the Source interface just after the upstream is expired" " from FRR1")
+ shutdown_bringup_interface(tgen, "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_ip_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_ip_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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=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_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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_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' "
+ )
+
+ done_flag = False
+ for retry in range(1, 11):
+ result = verify_upstream_iif(
+ tgen, "l1", "Unknown", source_i2, IGMP_JOIN_RANGE_1, expected=False
+ )
+ if result is not True:
+ done_flag = True
+ else:
+ continue
+ if done_flag:
+ logger.info("Expected Behavior: {}".format(result))
+ break
+
+ assert done_flag is True, (
+ "Testcase {} : Failed Error: \n "
+ "mroutes are still present, after waiting for 10 mins".format(tc_name)
+ )
+
+ step("No shut the Source interface just after the upstream is expired" " from FRR1")
+ shutdown_bringup_interface(tgen, "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_ip_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_ip_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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from"
+ " receiver interface of FRR1"
+ )
+
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ input_dict_2 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {intf_l1_i1: {"igmp": {"version": "2", "delete": True,}}}
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("IGMP join removed from FRR1 , verify using " "'show ip igmp groups json'")
+
+ dut = "l1"
+ interface = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n Groups are not"
+ " present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"]
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"]
+ intf_f1_i2 = topo["routers"]["f1"]["links"]["i2"]["interface"]
+ input_traffic = {
+ "l1": {"traffic_received": [intf_l1_r2], "traffic_sent": [intf_l1_i1]},
+ "f1": {"traffic_sent": [intf_f1_r2], "traffic_received": [intf_f1_i2]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure igmp 'ip igmp' and 'ip igmp version 2' from "
+ "receiver interface of FRR1"
+ )
+
+ input_dict_2 = {
+ "l1": {"igmp": {"interfaces": {intf_l1_i1: {"igmp": {"version": "2"}}}}}
+ }
+ 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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from"
+ " receiver interface of FRR1"
+ )
+
+ input_dict_2 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {intf_l1_i1: {"igmp": {"version": "2", "delete": True,}}}
+ }
+ }
+ }
+
+ result = create_igmp_config(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("IGMP join removed from FRR1 , verify using " "'show ip igmp groups json'")
+
+ dut = "l1"
+ interface = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n Groups are not"
+ " present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure igmp 'ip igmp' and 'ip igmp version 2' from "
+ "receiver interface of FRR1"
+ )
+
+ input_dict_2 = {
+ "l1": {"igmp": {"interfaces": {intf_l1_i1: {"igmp": {"version": "2"}}}}}
+ }
+ 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_ip_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_ip_mroutes(
+ tgen, dut, source, IGMP_JOIN_RANGE_1, iif, oil, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n routes are still"
+ " present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_verify_remove_add_igmp_commands_when_pim_configured_p0(request):
+ """
+ TC_34: Verify removing and adding IGMP commands when PIM is already
+ configured
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_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"}}}}}
+ }
+
+ result = verify_igmp_config(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from"
+ " receiver interface of FRR1"
+ )
+
+ input_dict_2 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {intf_l1_i1: {"igmp": {"version": "2", "delete": True,}}}
+ }
+ }
+ }
+
+ result = create_igmp_config(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verification: After removing the config CLI got removed "
+ "'show ip igmp interface ensxx json'"
+ )
+
+ result = verify_igmp_config(tgen, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "IGMP interface is not removed \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure 'ip igmp last-member-query-count 10' on FRR1" " receiver interface")
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {"igmp": {"query": {"last-member-query-count": 5}}}
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Remove 'ip igmp last-member-query-count 10' on FRR1" " receiver interface")
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "query": {"last-member-query-count": "", "delete": True}
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {"igmp": {"query": {"last-member-query-count": 2}}}
+ }
+ }
+ }
+ }
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure 'ip igmp last-member-query-interval 20' on FRR1"
+ " receiver interface"
+ )
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {"query": {"last-member-query-interval": 20}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Remove 'ip igmp last-member-query-count 10' on FRR1" " receiver interface")
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "query": {"last-member-query-interval": "", "delete": True}
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {"query": {"last-member-query-interval": 10}}
+ }
+ }
+ }
+ }
+ }
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_remove_add_pim_commands_when_igmp_configured_p1(request):
+ """
+ TC_35: Verify removing and adding PIM commands when IGMP is already
+ configured
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ 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"]
+ input_dict_1 = {"l1": {"pim": {"disable": intf_l1_i1}}}
+ result = create_pim_config(tgen, topo, input_dict_1)
+ 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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_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_ip_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_ip_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_ip_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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ 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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ 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"}}}}}
+ }
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "mroute still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ for data in input_dict_sg:
+ result = verify_ip_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")
+ kill_iperf(tgen)
+
+ 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, expected=False
+ )
+ assert isinstance(traffic_before, dict), (
+ "Testcase {} : Failed \n traffic_before is not dictionary \n "
+ "Error: {}".format(tc_name, result)
+ )
+
+ step("IGMP groups are remove from FRR1 node 'show ip igmp groups'")
+
+ dut = "f1"
+ result = verify_igmp_groups(
+ tgen, dut, intf_f1_i8, IGMP_JOIN_RANGE_1, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "IGMP groups still present still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step(
+ "After receiving the IGMP prune from FRR1 , verify traffic "
+ "immediately stopped for this receiver 'show ip multicast'"
+ )
+
+ intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"]
+ input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}}
+ traffic_after = verify_multicast_traffic(
+ tgen, input_traffic, return_traffic=True, expected=False
+ )
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_starg:
+ result = verify_ip_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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ 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"}}}}}
+ }
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "mroute still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ for data in input_dict_sg_i1:
+ result = verify_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify upstream after Shut the link from LHR to RP from RP node")
+
+ for data in input_dict_starg:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "upstream still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ for data in input_dict_sg_i1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("No shut the link from LHR to RP from RP node")
+
+ intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_f1, True)
+
+ step("Verify RP info after No shut the link from LHR to RP from RP node")
+ dut = "f1"
+ rp_address = "1.0.5.17"
+ SOURCE = "Static"
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "RP iif is not updated \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("Verify mroute after No shut the link from LHR to RP from RP node")
+
+ for data in input_dict_starg:
+ result = verify_ip_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_ip_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_ip_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)
+
+ kill_iperf(tgen, dut="i2", action="remove_traffic")
+
+ 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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step(" No shut the link from FHR to RP from RP node")
+
+ intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_l1, True)
+
+ step("Verify RP info after Noshut the link from FHR to RP from RP node")
+
+ dut = "l1"
+ rp_address = "1.0.5.17"
+ SOURCE = "Static"
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "RP iif is not updated \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("Verify mroute after Noshut the link from FHR to RP from RP node")
+
+ for data in input_dict_starg:
+ result = verify_ip_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_ip_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_ip_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")
+
+ kill_iperf(tgen, dut="i6", action="remove_traffic")
+
+ 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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step(" No shut the link from FHR to RP from FHR node")
+
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_r2, True)
+
+ step("Verify RP info after No Shut the link from FHR to RP from FHR node")
+ dut = "l1"
+ rp_address = "1.0.5.17"
+ SOURCE = "Static"
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "RP iif is not updated \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("Verify mroute after No Shut the link from FHR to RP from FHR node")
+
+ for data in input_dict_starg:
+ result = verify_ip_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_ip_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_ip_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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ 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"}}}}}
+ }
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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"}}}}}
+ }
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500)
+ 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_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n"
+ " Expected Behaviour: mroutes are cleared \n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ shutdown_bringup_interface(tgen, "r2", intf_r2_i3, True)
+
+ step(
+ "Verification: After No shut of receiver port , Verify (*,G)"
+ " and (S,G) got populated on LHR node (FRR1) using "
+ "'show ip mroute' 'show ip pim upstream'"
+ )
+
+ for data in input_dict_r2:
+ result = verify_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n"
+ "mroutes are cleared \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True)
+
+ step(
+ "Verification: After No shut of source port , Verify (*,G)"
+ " and (S,G) got populated on LHR node (FRR1) using "
+ "'show ip mroute' 'show ip pim upstream'"
+ )
+
+ for data in input_dict_l1:
+ result = verify_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n"
+ " mroutes are cleared \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ shutdown_bringup_interface(tgen, "r2", intf_r2_f1, True)
+
+ step(
+ "Verification: After No shut of source port , Verify (*,G)"
+ " and (S,G) got populated on LHR node (FRR1) using "
+ "'show ip mroute' 'show ip pim upstream'"
+ )
+
+ for data in input_dict_all:
+ result = verify_ip_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_ip_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_ip_mroutes(
+ tgen, dut, src_address, _IGMP_JOIN_RANGE, iif, oil, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n"
+ " mroutes are cleared \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ shutdown_bringup_interface(tgen, "l1", intf_l1_r2, True)
+
+ step(
+ "Verification: After No shut of source port , Verify (*,G)"
+ " and (S,G) got populated on LHR node (FRR1) using "
+ "'show ip mroute' 'show ip pim upstream'"
+ )
+
+ for data in input_dict_all:
+ result = verify_ip_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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500)
+ 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_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n"
+ " mroutes are cleared \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("No shut the receiver(l1) port in 1 min interval")
+
+ shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True)
+
+ step(
+ "Verification: After No shut of receiver port , Verify (*,G)"
+ " and (S,G) got populated on LHR node (FRR1) using "
+ "'show ip mroute' 'show ip pim upstream'"
+ )
+
+ for data in input_dict_l1:
+ result = verify_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n"
+ " mroutes are cleared \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("No shut the source(r2) port in 1 min interval")
+
+ shutdown_bringup_interface(tgen, "r2", intf_r2_i3, True)
+
+ step(
+ "Verification: After No shut of source port , Verify (*,G)"
+ " and (S,G) got populated on LHR and FHR using "
+ "'show ip mroute' 'show ip pim upstream'"
+ )
+
+ for data in input_dict_r2:
+ result = verify_ip_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_ip_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n"
+ " mroutes are cleared \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+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 0000000000..e8579e2a1e
--- /dev/null
+++ b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo4.py
@@ -0,0 +1,1122 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Following tests are covered to test multicast pim sm:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+
+1. TC:48 Verify mroute after configuring black-hole route for RP and source
+2. TC:49 Verify mroute when RP is reachable using default route
+3. TC:50 Verify mroute when LHR,FHR,RP and transit routers reachable
+ using default routes
+4. TC:52 Verify PIM nbr after changing interface ip
+5. TC:53 Verify IGMP interface updated with correct detail after changing interface config
+6. TC:54 Verify received and transmit hello stats are getting cleared after PIM nbr reset
+
+
+"""
+
+import os
+import re
+import sys
+import json
+import time
+import datetime
+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 mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ iperfSendIGMPJoin,
+ addKernelRoute,
+ reset_config_on_routers,
+ iperfSendTraffic,
+ kill_iperf,
+ shutdown_bringup_interface,
+ start_router,
+ stop_router,
+ apply_raw_config,
+ create_static_routes,
+ required_linux_kernel_version,
+ topo_daemons,
+)
+from lib.pim import (
+ create_pim_config,
+ create_igmp_config,
+ verify_igmp_groups,
+ verify_ip_mroutes,
+ clear_ip_pim_interface_traffic,
+ verify_igmp_config,
+ verify_pim_neighbors,
+ verify_pim_config,
+ verify_pim_interface,
+ verify_upstream_iif,
+ clear_ip_mroute,
+ verify_multicast_traffic,
+ verify_pim_rp_info,
+ verify_pim_interface_traffic,
+ verify_igmp_interface,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/multicast_pim_sm_topo4.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+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"
+
+
+class CreateTopo(Topo):
+ """
+ Test BasicTopo - topology 1
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # 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")
+
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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 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")
+
+ for router in topo["routers"].keys():
+ if "static_routes" in topo["routers"][router]:
+ static_routes = topo["routers"][router]["static_routes"]
+ for static_route in static_routes:
+ network = static_route["network"]
+ next_hop = static_route["next_hop"]
+ if type(network) is not list:
+ network = [network]
+
+ 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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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(
+ "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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_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_ip_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_ip_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: {}".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: {}".format(
+ tc_name, result
+ )
+
+ for data in input_dict_starg:
+ result = verify_ip_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: {}".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: {}".format(
+ tc_name, result
+ )
+
+ step("Configure default routes on c2")
+
+ intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["ipv4"].split("/")[0]
+
+ input_dict = {
+ "c2": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_f1_c2}]}
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("applying ip nht config on c2")
+
+ raw_config = {"c2": {"raw_config": ["ip nht resolve-via-default"]}}
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify RP info is NOT unknown after removing static route from c2 ")
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify (s,g) populated after adding default route ")
+
+ for data in input_dict_sg:
+ result = verify_ip_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_ip_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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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(
+ "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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_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_ip_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_ip_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: {}".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: {}".format(
+ tc_name, result
+ )
+
+ step("Configure default routes on all the nodes")
+
+ intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["ipv4"].split("/")[0]
+ intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["ipv4"].split("/")[0]
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["ipv4"].split("/")[0]
+ intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["ipv4"].split("/")[0]
+
+ input_dict = {
+ "c1": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_l1_c1}]},
+ "c2": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_f1_c2}]},
+ "r2": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_l1_r2}]},
+ "f1": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_r2_f1}]},
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("applying ip nht config on c2")
+
+ raw_config = {
+ "c1": {"raw_config": ["ip nht resolve-via-default"]},
+ "c2": {"raw_config": ["ip nht resolve-via-default"]},
+ "r2": {"raw_config": ["ip nht resolve-via-default"]},
+ "f1": {"raw_config": ["ip nht resolve-via-default"]},
+ "l1": {"raw_config": ["ip nht resolve-via-default"]},
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify RP info Not unknown after removing static route from c2 ")
+ dut = "c2"
+ step("Verify RP info is NOT unknown after removing static route from c2 ")
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify (s,g) populated after adding default route ")
+
+ for data in input_dict_sg:
+ result = verify_ip_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_ip_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)
+
+ # Creating configuration from JSON
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_ip_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(
+ "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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1)
+ 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 = config_to_send_igmp_join_and_traffic(
+ tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500)
+ 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_ip_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_ip_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"]
+
+ step("verify before stats on C1")
+ state_dict = {"c1": {intf_c1_l1: ["helloTx", "helloRx"],}}
+
+ 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)
+
+ step("Flap PIM nbr while doing interface c1-l1 interface shut from f1 side")
+ shutdown_bringup_interface(tgen, "c1", intf_c1_l1, False)
+
+ step(
+ "After shut of local interface from c1 , verify rx/tx hello counters are cleared on c1 side"
+ "verify using 'show ip pim interface traffic'"
+ )
+ shutdown_bringup_interface(tgen, "c1", intf_c1_l1, True)
+
+ step("verify stats after on c1")
+ 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_state_incremented(c1_state_before, c1_state_after)
+ assert (
+ result is not True
+ ), "Testcase{} : Failed Error: {}" "stats incremented".format(tc_name, result)
+
+ step("verify before stats on l1")
+ l1_state_dict = {"l1": {intf_l1_c1: ["helloTx", "helloRx"],}}
+
+ l1_state_before = verify_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")
+ l1_state_after = verify_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)
+
+ step("verify stats not increamented on l1")
+ result = verify_state_incremented(l1_state_before, l1_state_after)
+ 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 = 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)
+
+ 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")
+ 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_state_incremented(c1_state_before, c1_state_after)
+ assert (
+ result is not 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 0000000000..e69de29bb2
--- /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 0000000000..6d6c047b00
--- /dev/null
+++ b/tests/topotests/multicast-pim-static-rp-topo1/multicast_pim_static_rp.json
@@ -0,0 +1,93 @@
+{
+ "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": {
+ "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": ["224.0.0.0/4"]}]
+ },
+ "igmp": {"interfaces": {"r1-r0-eth0": {"igmp": {"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": {
+ "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": {
+ "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.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": {
+ "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.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 0000000000..8dfdd50527
--- /dev/null
+++ b/tests/topotests/multicast-pim-static-rp-topo1/test_multicast_pim_static_rp.py
@@ -0,0 +1,3810 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Following tests are covered to test Multicast basic functionality:
+
+Topology:
+
+ _______r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+ | |
+ |_____________|
+ r4
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+TC_1 : Verify upstream interfaces(IIF) and join state are updated properly
+ after adding and deleting the static RP
+TC_2 : Verify IIF and OIL in "show ip pim state" updated properly after
+ adding and deleting the static RP
+TC_3: (*, G) Mroute entry are cleared when static RP gets deleted
+TC_4: Verify (*,G) prune is send towards the RP after deleting the static RP
+TC_5: Verify OIF entry for RP is cleared when RP becomes unreachable
+TC_6: Verify IIF and OIL in "show ip pim state" updated properly when RP
+ becomes unreachable
+TC_7 : Verify upstream interfaces(IIF) and join state are updated properly
+ after adding and deleting the static RP
+TC_8: Verify (*,G) prune is send towards the RP when RP becomes unreachable
+TC_9 : Verify RP configured after IGMP join received, PIM join towards RP is
+ sent immediately
+TC_10 : Verify RP becomes reachable after IGMP join received, PIM join
+ towards RP is sent immediately
+TC_11 : Verify PIM join send towards the higher preferred RP
+TC_12 : Verify PIM prune send towards the lower preferred RP
+TC_13 : Verify RPF interface is updated in mroute (kernel) when higher
+ preferred overlapping RP configured
+TC_14 : Verify IIF and OIL in "show ip pim state" updated properly when higher
+ preferred overlapping RP configured
+TC_15 : Verify upstream interfaces(IIF) and join state are updated when higher
+ preferred overlapping RP is configured
+TC_16 : Verify join is send to lower preferred RP, when higher preferred RP
+ gets deleted
+TC_17 : Verify prune is send to higher preferred RP when higher preferred RP
+ gets deleted
+TC_18 : Verify RPF interface updated in mroute when higher preferred RP gets
+ deleted
+TC_19 : Verify IIF and OIL in "show ip pim state" updated when higher
+ preferred overlapping RP is deleted
+TC_20 : Verfiy 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 inteface
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+from time import sleep
+import datetime
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# Required to instantiate the topology builder class.
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ iperfSendIGMPJoin,
+ iperfSendTraffic,
+ addKernelRoute,
+ shutdown_bringup_interface,
+ kill_router_daemons,
+ start_router_daemons,
+ create_static_routes,
+ kill_iperf,
+ topo_daemons,
+)
+from lib.pim import (
+ create_pim_config,
+ verify_igmp_groups,
+ verify_upstream_iif,
+ verify_join_state_and_timer,
+ verify_ip_mroutes,
+ verify_pim_neighbors,
+ verify_pim_interface_traffic,
+ verify_pim_rp_info,
+ verify_pim_state,
+ clear_ip_pim_interface_traffic,
+ clear_ip_igmp_interfaces,
+ clear_ip_pim_interfaces,
+ clear_ip_mroute,
+ clear_ip_mroute_verify,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology and configuration creation
+jsonFile = "{}/multicast_pim_static_rp.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ logger.info("Could not read file:", jsonFile)
+
+# 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"
+
+
+class CreateTopo(Topo):
+ """
+ Test BasicTopo - topology 1
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ topology = """
+
+ _______r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+ | |
+ |_____________|
+ r4
+
+ """
+ logger.info("Master Topology: \n {}".format(topology))
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Verify PIM neighbors
+ result = verify_pim_neighbors(tgen, topo)
+ assert result is True, "setup_module :Failed \n Error:" " {}".format(result)
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+def config_to_send_igmp_join_and_traffic(tgen, tc_name):
+ """
+ API to do pre-configuration to send IGMP join and multicast
+ traffic
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ * `tc_name`: caller test case name
+ """
+
+ step("r0: Add route to kernal")
+ result = addKernelRoute(tgen, "r0", "r0-r1-eth0", GROUP_RANGE_ALL)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r5: Add route to kernal")
+ result = addKernelRoute(tgen, "r5", "r5-r3-eth0", GROUP_RANGE_ALL)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ rnode = tgen.routers()["r1"]
+ rnode.run("ip route add 10.0.6.0/24 via 10.0.2.2")
+ rnode = tgen.routers()["r2"]
+ rnode.run("ip route add 10.0.6.0/24 via 10.0.4.2")
+ rnode = tgen.routers()["r4"]
+ rnode.run("ip route add 10.0.6.0/24 via 10.0.5.1")
+
+ router_list = tgen.routers()
+ for router in router_list.keys():
+ rnode = router_list[router]
+ rnode.run("echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter")
+
+ return True
+
+
+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 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")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Verify show ip pim interface traffic without any IGMP join")
+ state_dict = {"r1": {"r1-r2-eth1": ["pruneTx"]}}
+
+ state_before = 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("r0 : Send IGMP join")
+ result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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 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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Verify PIM state")
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Verify ip mroutes")
+ result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Verify show ip pim interface traffic without any IGMP join")
+ state_after = verify_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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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 = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500)
+ 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_ip_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_ip_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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r2-eth1"
+ result = verify_ip_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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = 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("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 = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: upstream IIF should be unknown , verify using show ip pim" "upstream")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "r1: join state should not be joined and join timer should stop,"
+ "verify using show ip pim upstream"
+ )
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "r1: (*,G) prune is sent towards the RP interface, verify using"
+ "show ip pim interface traffic"
+ )
+ state_after = verify_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_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behavior: {}".format(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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("joinTx value before join sent")
+ state_dict = {"r1": {"r1-r2-eth1": ["joinTx"]}}
+ 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("r0 : Send IGMP join (225.1.1.1) to r1, when rp is not configured" "in r1")
+ result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Verify upstream join state and join timer")
+
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Verify PIM state")
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Verify ip mroutes")
+ result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Configure static RP")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": GROUP_RANGE_ALL,}]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify rp-info")
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify PIM state")
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify ip mroutes")
+ result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ logger.info("Expected behavior: {}".format(result))
+
+ state_after = verify_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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = 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("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 = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1 : Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1 : Verify PIM state")
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1 : Verify ip mroutes")
+ result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Make RP reachable")
+ intf = "r1-r2-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ intf = "r1-r3-eth2"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ intf = "r1-r4-eth3"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1 : Verify rp-info")
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify PIM state")
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify ip mroutes")
+ result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ logger.info("Expected behavior: {}".format(result))
+
+ step("r1 : Verify pim interface traffic")
+ state_after = verify_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 : Verfiy 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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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-eth1"
+ 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 = 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("r0 : Send IGMP join for 225.1.1.1")
+ result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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 = verify_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_ip_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_ip_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 = 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("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 Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "r1 : Verify RPF interface updated in mroute when higher preferred"
+ "RP gets deleted"
+ )
+ iif = "r1-r2-eth1"
+ result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ logger.info("Expected behavior: {}".format(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 : Verfiy 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 = verify_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_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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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 = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500)
+ 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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_ip_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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Send IGMP join")
+ result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_ip_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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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 = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500)
+ 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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_ip_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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Send IGMP join")
+ result = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_ip_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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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 = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500)
+ 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_ip_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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r2: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_ip_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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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 = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500)
+ 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_ip_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_ip_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_ip_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_ip_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)
+
+
+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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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 = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS, 32, 2500)
+ 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_ip_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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_ip_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)
+ uptime_before = verify_ip_mroutes(
+ tgen, dut, STAR, GROUP_ADDRESS, iif, oil, return_uptime=True, wait=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)
+
+ uptime_after = verify_ip_mroutes(
+ tgen, dut, STAR, GROUP_ADDRESS, iif, oil, return_uptime=True, wait=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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS_LIST, join_interval=1)
+ 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 = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS_LIST, 32, 2500)
+ 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_ip_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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r2: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_ip_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_ip_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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r2: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_ip_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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS_LIST, join_interval=1)
+ 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 = iperfSendTraffic(tgen, "r5", GROUP_ADDRESS_LIST, 32, 2500)
+ 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_ip_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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r2: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_ip_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_ip_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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r4: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_ip_mroutes(
+ tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_ip_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_ip_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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r2: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_ip_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_ip_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_ip_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_ip_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 Error: {}".format(
+ tc_name, result
+ )
+
+ step("r4: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_ip_mroutes(
+ tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_ip_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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ # 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 = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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_ip_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_ip_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_ip_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_ip_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_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not 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_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not 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_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: No shutdown the link from R1 to R3 from R3 node")
+ dut = "r3"
+ intf = "r3-r1-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1: Verify (*, G) ip mroutes")
+ dut = "r1"
+ iif = "r1-r3-eth2"
+ oif = "r1-r0-eth0"
+ result = verify_ip_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_ip_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_ip_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_ip_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_ip_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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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_ip_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_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r2: Verify (*, G) ip mroutes cleared")
+ dut = "r2"
+ iif = "lo"
+ oif = "r2-r1-eth0"
+ result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_delete_RP_shut_noshut_RP_interface_p1(request):
+ """
+ TC_32_P1: Verify RP info and (*,G) mroute after deleting the RP and shut/
+ no shut the RPF inteface
+
+ 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)
+ kill_iperf(tgen)
+ clear_ip_mroute(tgen)
+ clear_ip_pim_interface_traffic(tgen, topo)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+ result = config_to_send_igmp_join_and_traffic(tgen, tc_name)
+ assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result)
+
+ 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 = iperfSendIGMPJoin(tgen, "r0", GROUP_ADDRESS, join_interval=1)
+ 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_ip_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_ip_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_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r2: Verify (*, G) ip mroutes cleared")
+ dut = "r2"
+ iif = "lo"
+ oif = "r2-r1-eth0"
+ result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, 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-dual-stack/test_ospf_dual_stack.dot b/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.dot
new file mode 100644
index 0000000000..2c6d0aab16
--- /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 0000000000..44efda8390
--- /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 0000000000..c8a3ce783b
--- /dev/null
+++ b/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.json
@@ -0,0 +1,255 @@
+{
+ "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
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "1.1.1.1",
+ "neighbors": {
+ "r3": {}
+ }
+ },
+ "ospf6": {
+ "router_id": "1.1.1.1",
+ "neighbors": {
+ "r3": {
+ "area": "1.1.1.1"
+ }
+ }
+ }
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "2.2.2.2",
+ "neighbors": {
+ "r3": {},
+ "r4": {}
+ }
+ },
+ "ospf6": {
+ "router_id": "2.2.2.2",
+ "neighbors": {
+ "r3": { "area": "1.1.1.1" },
+ "r4": { "area": "0.0.0.0" }
+ }
+ }
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "3.3.3.3",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r4": {}
+ }
+ },
+ "ospf6": {
+ "router_id": "3.3.3.3",
+ "neighbors": {
+ "r1": { "area": "1.1.1.1" },
+ "r2": { "area": "1.1.1.1" },
+ "r4": { "area": "0.0.0.0" }
+ }
+ }
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "4.4.4.4",
+ "neighbors": {
+ "r2": {},
+ "r3": {},
+ "r5": {}
+ }
+ },
+ "ospf6": {
+ "router_id": "4.4.4.4",
+ "neighbors": {
+ "r2": { "area": "0.0.0.0" },
+ "r3": { "area": "0.0.0.0" },
+ "r5": { "area": "2.2.2.2" }
+ }
+ }
+ },
+ "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"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "5.5.5.5",
+ "neighbors": {
+ "r4": {}
+ }
+ },
+ "ospf6": {
+ "router_id": "5.5.5.5",
+ "neighbors": {
+ "r4": { "area": "2.2.2.2" }
+ }
+ }
+ }
+ }
+}
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 0000000000..5e7802fa04
--- /dev/null
+++ b/tests/topotests/ospf-dual-stack/test_ospf_dual_stack.py
@@ -0,0 +1,152 @@
+#!/usr/bin/python
+
+import os
+import sys
+import time
+import pytest
+import json
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ stop_router,
+ start_router,
+ verify_rib,
+ create_static_routes,
+ step,
+ start_router_daemons,
+ shutdown_bringup_interface,
+ topo_daemons,
+ create_prefix_lists,
+ create_interfaces_cfg,
+ run_frr_cmd,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+from lib.ospf import (
+ verify_ospf_neighbor,
+ verify_ospf6_neighbor,
+ create_router_ospf,
+ create_router_ospf6,
+ verify_ospf_summary,
+ redistribute_ospf,
+ verify_ospf_database,
+)
+
+# Global variables
+topo = None
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/test_ospf_dual_stack.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+
+class CreateTopo(Topo):
+ """Test topology builder."""
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """Sets up the pytest environment."""
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether OSPF converged
+ ospf_covergence_ipv4 = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence_ipv4 is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence_ipv4
+ )
+
+ # Api call verify whether OSPF6 converged
+ ospf_covergence_ipv6 = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence_ipv6 is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence_ipv6
+ )
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+ tgen = get_topogen()
+ # Stop topology and remove tmp files
+ tgen.stop_topology()
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#
+# ##################################
+# Test cases start here.
+# ##################################
+#
+#
+def test_ospf_dual_stack(request):
+ """OSPF test dual stack."""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don't run this test if we have any failure.
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+
+ step("Bring up the base configuration as per the JSON topology")
+ reset_config_on_routers(tgen)
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf-tilfa-topo1/__init__.py b/tests/topotests/ospf-tilfa-topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /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 0000000000..eaef49225f
--- /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 0000000000..0ad2aaeade
--- /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 0000000000..0ad2aaeade
--- /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 0000000000..968570e193
--- /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 0000000000..0ad2aaeade
--- /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 0000000000..46a80d298e
--- /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 0000000000..bf0e77a17b
--- /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 0000000000..7548aad7f8
--- /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 0000000000..add2933571
--- /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 0000000000..6258295b6f
--- /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 0000000000..1bb64bc585
--- /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 0000000000..ad02214017
--- /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 0000000000..306f0d4925
--- /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 0000000000..1b95858f53
--- /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 0000000000..46f759580e
--- /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 0000000000..eb3ad5d995
--- /dev/null
+++ b/tests/topotests/ospf-tilfa-topo1/test_ospf_tilfa_topo1.py
@@ -0,0 +1,242 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_tilfa_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_ospf_tilfa_topo1.py:
+
+This topology is intentionally kept simple, its main purpose is to verify that
+generated backup label stacks are inserted correctly into the RIB. For fancy
+topologies please use the unit test framework provided in `/tests/ospfd`.
+
+
+ +---------+ +---------+
+ | | | |
+ 10.0.1.0/24 eth+rt1| RT2 |eth+rt4 eth+rt2| RT2 |
+ +---------------------+ 2.2.2.2 +---------------------+ 4.4.4.4 |
+ | | | 10.0.3.0/24 | |
+ |eth+rt2 +---------+ +---------+
+ +---------+ eth+rt5|
+ | | |
+ | RT1 | 10.0.5.0/24|
+ | 1.1.1.1 | |
+ | | |
+ +---------+ eth+rt4|
+ |eth+rt3 +---------+ +---------+
+ | | | 10.0.4.0/24 | |
+ +---------------------+ RT3 +---------------------+ RT5 |
+ 10.0.2.0/24 eth+rt1| 3.3.3.3 |eth+rt5 eth-rt3| 5.5.5.5 |
+ | | | |
+ +---------+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import re
+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.
+from mininet.topo import Topo
+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ #
+ # 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(TemplateTopo, 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-vrf/test_ospf_topo1_vrf.py b/tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py
index 6d44d02e5e..2421b312d2 100644
--- a/tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py
+++ b/tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py
@@ -320,8 +320,10 @@ def test_ospf_link_down_kernel_route():
# 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
+ assertmsg = (
+ 'OSPF IPv4 route mismatch in router "{}" after link down: {}'.format(
+ router.name, diff
+ )
)
assert result, assertmsg
diff --git a/tests/topotests/ospf-topo1/test_ospf_topo1.py b/tests/topotests/ospf-topo1/test_ospf_topo1.py
index 95193afb2a..24806dd8fc 100644
--- a/tests/topotests/ospf-topo1/test_ospf_topo1.py
+++ b/tests/topotests/ospf-topo1/test_ospf_topo1.py
@@ -310,7 +310,10 @@ def test_ospf_json():
# 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,}
+ {
+ "areaIfActiveCounter": 2,
+ "areaIfTotalCounter": 2,
+ }
)
# router 3 has an additional area
@@ -372,16 +375,25 @@ def test_ospf_link_down_kernel_route():
}
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,}
+ {
+ "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,}
+ {
+ "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,}
+ {
+ "10.0.3.0/24": None,
+ }
)
assertmsg = 'OSPF IPv4 route mismatch in router "{}" after link down'.format(
router.name
@@ -443,12 +455,17 @@ def test_ospf6_link_down_kernel_route():
)
elif router.name == "r3" or router.name == "r4":
expected.update(
- {"2001:db8:1::/64": None, "2001:db8:2::/64": None,}
+ {
+ "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,}
+ {
+ "2001:db8:3::/64": None,
+ }
)
assertmsg = 'OSPF IPv6 route mismatch in router "{}" after link down'.format(
router.name
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 0000000000..032acb5341
--- /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/r3/ip_6_address.nhg.ref b/tests/topotests/ospf6-topo1/r3/ip_6_address.nhg.ref
new file mode 100644
index 0000000000..101fcc95b4
--- /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/r4/ip_6_address.nhg.ref b/tests/topotests/ospf6-topo1/r4/ip_6_address.nhg.ref
new file mode 100644
index 0000000000..4f11670ce3
--- /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/test_ospf6_topo1.py b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py
index 8e3a329f10..c3efb6ff22 100644
--- a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py
+++ b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py
@@ -383,7 +383,15 @@ def test_linux_ipv6_kernel_routingTable():
"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_shutdown_check_stderr():
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 0000000000..ed199f181b
--- /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_p2mp.json b/tests/topotests/ospf_basic_functionality/ospf_p2mp.json
new file mode 100644
index 0000000000..40815f36f3
--- /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/test_ospf_authentication.py b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py
index e92baefabf..9c3be58937 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py
@@ -29,6 +29,7 @@ import pytest
from time import sleep
from copy import deepcopy
import json
+from lib.topotest import frr_unicode
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
@@ -318,7 +319,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request):
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(unicode(intf_ip.split("/")[0])) + 3
+ 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)
@@ -529,7 +530,7 @@ def test_ospf_authentication_md5_tc29_p1(request):
intf_ip = topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"]
topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] = str(
- IPv4Address(unicode(intf_ip.split("/")[0])) + 3
+ 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)
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 0000000000..37b7528490
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py
@@ -0,0 +1,576 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+from copy import deepcopy
+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, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ shutdown_bringup_interface,
+ topo_daemons,
+ 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_topo_from_json, build_config_from_json
+from ipaddress import IPv4Address
+
+
+
+# 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.
+ """
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospf_chaos.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+class CreateTopo(Topo):
+ """
+ Test topology builder.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+def test_ospf_chaos_tc31_p1(request):
+ """Verify ospf functionality after restart ospfd."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute "
+ "to OSPF using route map.")
+
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK['ipv4'][0],
+ "no_of_ip": 5,
+ "next_hop": 'Null0',
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ ospf_red_r0 = {
+ "r0": {
+ "ospf": {
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Verify OSPF neighbors after base config is done.")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, ("setup_module :Failed \n Error:"
+ " {}".format(ospf_covergence))
+
+ step("Verify that route is advertised to R1.")
+ dut = 'r1'
+ protocol = 'ospf'
+ nh = topo['routers']['r0']['links']['r1']['ipv4'].split('/')[0]
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Kill OSPFd daemon on R0.")
+ kill_router_daemons(tgen, "r0", ["ospfd"])
+
+ step("Verify OSPF neighbors are down after killing ospfd in R0")
+ dut = 'r0'
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut,
+ expected=False)
+ assert ospf_covergence is not True, ("setup_module :Failed \n Error:"
+ " {}".format(ospf_covergence))
+
+ step("Verify that route advertised to R1 are deleted from RIB and FIB.")
+ dut = 'r1'
+ protocol = 'ospf'
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n 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)
+
+ step("Bring up OSPFd daemon on R0.")
+ start_router_daemons(tgen, "r0", ["ospfd"])
+
+ step("Verify OSPF neighbors are up after bringing back ospfd in R0")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, ("setup_module :Failed \n Error:"
+ " {}".format(ospf_covergence))
+
+ step(
+ "All the neighbours are up and routes are installed before the"
+ " restart. Verify OSPF route table and ip route table.")
+ dut = 'r1'
+ protocol = 'ospf'
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol,
+ next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Kill OSPFd daemon on R1.")
+ kill_router_daemons(tgen, "r1", ["ospfd"])
+
+ step("Verify OSPF neighbors are down after killing ospfd in R1")
+ dut = 'r1'
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut,
+ expected=False)
+ assert ospf_covergence is not True, ("setup_module :Failed \n Error:"
+ " {}".format(ospf_covergence))
+
+ step("Bring up OSPFd daemon on R1.")
+ start_router_daemons(tgen, "r1", ["ospfd"])
+
+ step("Verify OSPF neighbors are up after bringing back ospfd in R1")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, ("setup_module :Failed \n Error:"
+ " {}".format(ospf_covergence))
+
+ step(
+ "All the neighbours are up and routes are installed before the"
+ " restart. Verify OSPF route table and ip route table.")
+
+ dut = 'r1'
+ protocol = 'ospf'
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol,
+ next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_chaos_tc32_p1(request):
+ """Verify ospf functionality after restart FRR service. """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute "
+ "to OSPF using route map.")
+
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK['ipv4'][0],
+ "no_of_ip": 5,
+ "next_hop": 'Null0',
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ ospf_red_r0 = {
+ "r0": {
+ "ospf": {
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Verify OSPF neighbors after base config is done.")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, ("setup_module :Failed \n Error:"
+ " {}".format(ospf_covergence))
+
+ step("Verify that route is advertised to R1.")
+ dut = 'r1'
+ protocol = 'ospf'
+
+ nh = topo['routers']['r0']['links']['r1']['ipv4'].split('/')[0]
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol,
+ next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Restart frr on R0")
+ stop_router(tgen, 'r0')
+ start_router(tgen, 'r0')
+
+ step("Verify OSPF neighbors are up after restarting R0")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, ("setup_module :Failed \n Error:"
+ " {}".format(ospf_covergence))
+
+ step(
+ "All the neighbours are up and routes are installed before the"
+ " restart. Verify OSPF route table and ip route table.")
+ dut = 'r1'
+ protocol = 'ospf'
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol,
+ next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Restart frr on R1")
+ stop_router(tgen, 'r1')
+ start_router(tgen, 'r1')
+
+ step("Verify OSPF neighbors are up after restarting R1")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, ("setup_module :Failed \n Error:"
+ " {}".format(ospf_covergence))
+
+ step(
+ "All the neighbours are up and routes are installed before the"
+ " restart. Verify OSPF route table and ip route table.")
+ dut = 'r1'
+ protocol = 'ospf'
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol,
+ next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_chaos_tc34_p1(request):
+ """
+ verify ospf functionality when staticd is restarted.
+
+ Verify ospf functionalitywhen staticroutes are
+ redistributed & Staticd is restarted.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute "
+ "to OSPF using route map.")
+
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK['ipv4'][0],
+ "no_of_ip": 5,
+ "next_hop": 'Null0',
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ ospf_red_r0 = {
+ "r0": {
+ "ospf": {
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Verify OSPF neighbors after base config is done.")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, ("setup_module :Failed \n Error:"
+ " {}".format(ospf_covergence))
+
+ step("Verify that route is advertised to R1.")
+ dut = 'r1'
+ protocol = 'ospf'
+ nh = topo['routers']['r0']['links']['r1']['ipv4'].split('/')[0]
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol,
+ next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Kill staticd daemon on R0.")
+ kill_router_daemons(tgen, "r0", ["staticd"])
+
+ step("Verify that route advertised to R1 are deleted from RIB and FIB.")
+ dut = 'r1'
+ protocol = 'ospf'
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n 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)
+
+ step("Bring up staticd daemon on R0.")
+ start_router_daemons(tgen, "r0", ["staticd"])
+
+ step("Verify OSPF neighbors are up after bringing back ospfd in R0")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, ("setup_module :Failed \n Error:"
+ " {}".format(ospf_covergence))
+
+ step(
+ "All the neighbours are up and routes are installed before the"
+ " restart. Verify OSPF route table and ip route table.")
+ dut = 'r1'
+ protocol = 'ospf'
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol,
+ next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Kill staticd daemon on R1.")
+ kill_router_daemons(tgen, "r1", ["staticd"])
+
+ step("Bring up staticd daemon on R1.")
+ start_router_daemons(tgen, "r1", ["staticd"])
+
+ step("Verify OSPF neighbors are up after bringing back ospfd in R1")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, ("setup_module :Failed \n Error:"
+ " {}".format(ospf_covergence))
+
+ step(
+ "All the neighbours are up and routes are installed before the"
+ " restart. Verify OSPF route table and ip route table.")
+
+ dut = 'r1'
+ protocol = 'ospf'
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol,
+ next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py
index 3b37b8a92f..441368e8fa 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py
@@ -65,6 +65,7 @@ from lib.ospf import (
verify_ospf_rib,
create_router_ospf,
verify_ospf_interface,
+ redistribute_ospf,
)
topo = None
@@ -184,38 +185,6 @@ def teardown_module(mod):
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.
# ##################################
@@ -252,7 +221,11 @@ def test_ospf_ecmp_tc16_p0(request):
input_dict = {
"r0": {
"static_routes": [
- {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0",}
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
]
}
}
@@ -260,7 +233,7 @@ def test_ospf_ecmp_tc16_p0(request):
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
dut = "r0"
- red_static(dut)
+ redistribute_ospf(tgen, topo, dut, "static")
step("Verify that route in R2 in stalled with 8 next hops.")
nh = []
@@ -341,7 +314,7 @@ def test_ospf_ecmp_tc16_p0(request):
step(" Un configure static route on R0")
dut = "r0"
- red_static(dut, config=False)
+ redistribute_ospf(tgen, topo, dut, "static", delete=True)
# Wait for R0 to flush external LSAs.
sleep(10)
@@ -372,7 +345,7 @@ def test_ospf_ecmp_tc16_p0(request):
step("Re configure the static route in R0.")
dut = "r0"
- red_static(dut)
+ redistribute_ospf(tgen, topo, dut, "static")
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
@@ -415,7 +388,11 @@ def test_ospf_ecmp_tc17_p0(request):
input_dict = {
"r0": {
"static_routes": [
- {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0",}
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
]
}
}
@@ -423,7 +400,7 @@ def test_ospf_ecmp_tc17_p0(request):
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
dut = "r0"
- red_static(dut)
+ redistribute_ospf(tgen, topo, dut, "static")
step("Verify that route in R2 in stalled with 2 next hops.")
@@ -442,7 +419,7 @@ def test_ospf_ecmp_tc17_p0(request):
step(" Un configure static route on R0")
dut = "r0"
- red_static(dut, config=False)
+ redistribute_ospf(tgen, topo, dut, "static", delete=True)
# sleep till the route gets withdrawn
sleep(10)
@@ -472,7 +449,7 @@ def test_ospf_ecmp_tc17_p0(request):
step("Reconfigure the static route in R0.Change ECMP value to 2.")
dut = "r0"
- red_static(dut)
+ redistribute_ospf(tgen, topo, dut, "static")
step("Configure cost on R0 as 100")
r0_ospf_cost = {"r0": {"links": {"r1": {"ospf": {"cost": 100}}}}}
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py
index 967bc44879..2da1dcd21a 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py
@@ -66,6 +66,7 @@ from lib.ospf import (
verify_ospf_rib,
create_router_ospf,
verify_ospf_interface,
+ redistribute_ospf,
)
from ipaddress import IPv4Address
@@ -187,42 +188,6 @@ def teardown_module():
pass
-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)
-
-
-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.
# ##################################
@@ -275,7 +240,7 @@ def test_ospf_lan_ecmp_tc18_p0(request):
)
dut = rtr
- red_static(dut)
+ redistribute_ospf(tgen, topo, dut, "static")
step(
"Verify that route in R0 in stalled with 8 hops. "
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py
index 1357a86c81..dac32090bc 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py
@@ -29,6 +29,7 @@ import pytest
import json
from copy import deepcopy
import ipaddress
+from lib.topotest import frr_unicode
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
@@ -182,42 +183,6 @@ def teardown_module():
pass
-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)
-
-
-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.
# ##################################
@@ -468,7 +433,7 @@ def test_ospf_lan_tc1_p0(request):
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(unicode(intf_ip.split("/")[0])) + 3
+ 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)
@@ -601,7 +566,7 @@ def test_ospf_lan_tc2_p0(request):
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(unicode(intf_ip.split("/")[0])) + 3
+ 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)
@@ -652,7 +617,7 @@ def test_ospf_lan_tc2_p0(request):
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(unicode(intf_ip.split("/")[0])) + 3
+ 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)
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py
index 82a34d046c..3644bff3dc 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py
@@ -30,6 +30,7 @@ from lib.ospf import (
verify_ospf_rib,
create_router_ospf,
verify_ospf_interface,
+ redistribute_ospf,
)
from lib.topojson import build_topo_from_json, build_config_from_json
from lib.topolog import logger
@@ -181,38 +182,6 @@ def teardown_module(mod):
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.
# ##################################
@@ -268,7 +237,7 @@ def test_ospf_learning_tc15_p0(request):
step("Redistribute static route in R2 ospf.")
dut = "r2"
- red_static(dut)
+ redistribute_ospf(tgen, topo, dut, "static")
step("Verify that Type 5 LSA is originated by R2.")
dut = "r0"
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 0000000000..c90275ecb0
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py
@@ -0,0 +1,416 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import json
+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 mininet.topo import Topo
+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,
+ create_static_routes,
+ step,
+ create_route_maps,
+ shutdown_bringup_interface,
+ create_interfaces_cfg,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+from lib.ospf import (
+ verify_ospf_neighbor,
+ config_ospf_interface,
+ clear_ospf,
+ verify_ospf_rib,
+ create_router_ospf,
+ verify_ospf_interface,
+ verify_ospf_database,
+)
+
+# Global variables
+topo = None
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospf_p2mp.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. OSPF P2MP -Verify state change events on p2mp network.
+ """
+
+
+class CreateTopo(Topo):
+ """
+ Test topology builder.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_p2mp_tc1_p0(request):
+ """OSPF IFSM -Verify state change events on p2mp network."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+ step(
+ "Verify that OSPF is subscribed to multi cast services "
+ "(All SPF, all DR Routers)."
+ )
+ step("Verify that interface is enabled in ospf.")
+ step("Verify that config is successful.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {"ospf": {"mcastMemberOspfAllRouters": True, "ospfEnabled": True}}
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on the R0 interface")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str(
+ IPv4Address(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(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)
+
+
+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
index 04b1f4b878..ceadb3975b 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py
@@ -62,6 +62,7 @@ from lib.ospf import (
verify_ospf_rib,
create_router_ospf,
verify_ospf_database,
+ redistribute_ospf,
)
# Global variables
@@ -197,6 +198,7 @@ def teardown_module(mod):
# Test cases start here.
# ##################################
+
def test_ospf_routemaps_functionality_tc19_p0(request):
"""
OSPF Route map - Verify OSPF route map support functionality.
@@ -215,121 +217,76 @@ def test_ospf_routemaps_functionality_tc19_p0(request):
"r0": {
"static_routes": [
{
- "network": NETWORK['ipv4'][0],
+ "network": NETWORK["ipv4"][0],
"no_of_ip": 5,
- "next_hop": 'Null0',
+ "next_hop": "Null0",
}
]
}
}
result = create_static_routes(tgen, input_dict)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(
- tc_name, result)
+ 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)
-
- dut = 'r1'
- lsid = NETWORK['ipv4'][0].split("/")[0]
- rid = routerids[0]
- protocol = 'ospf'
+ 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)
+ 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)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- ospf_red_r1 = {
- "r0": {
- "ospf": {
- "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)
+ 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 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"
- }
+ "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)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
# Create route map
routemaps = {
- "r0": {
- "route_maps": {
- "rmap_ipv4": [{
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
"action": "permit",
- "match": {
- "ipv4": {
- "prefix_lists":
- "pf_list_1_ipv4"
- }
- }
- }]
- }
+ "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)
+ 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 using route map rmap1"
+ )
- 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)
+ 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
@@ -337,65 +294,54 @@ def test_ospf_routemaps_functionality_tc19_p0(request):
"r0": {
"prefix_lists": {
"ipv4": {
- "pf_list_1_ipv4": [{
- "seqid": 10,
- "network": NETWORK['ipv4'][1],
- "action": "permit"
- },
- {
- "seqid": 11,
- "network": "any",
- "action": "deny"
- }
+ "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)
+ 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'
+ dut = "r1"
input_dict = {
"r0": {
"static_routes": [
- {
- "network": NETWORK['ipv4'][1],
- "no_of_ip": 1,
- "next_hop": 'Null0'
- }
+ {"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)
+ 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)
+ 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'
- }
+ {"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 Error: {}".format(
- tc_name, result)
+ tc_name, result
+ )
- result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol,
- expected=False)
+ 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)
+ tc_name, result
+ )
step("Delete and reconfigure prefix list.")
# Create ip prefix list
@@ -403,114 +349,101 @@ def test_ospf_routemaps_functionality_tc19_p0(request):
"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
- }
+ "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)
+ 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)
+ 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'
- }
+ {"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 Error: {}".format(
- tc_name, result)
+ tc_name, result
+ )
- result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol,
- expected=False)
+ 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)
+ 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"
- }
+ "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)
+ 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'
+ dut = "r1"
input_dict = {
"r0": {
"static_routes": [
- {
- "network": NETWORK['ipv4'][1],
- "no_of_ip": 1,
- "next_hop": 'Null0'
- }
+ {"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)
+ 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)
+ 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'
- }
+ {"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 Error: {}".format(
- tc_name, result)
+ tc_name, result
+ )
- result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol,
- expected=False)
+ 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)
+ tc_name, result
+ )
write_test_footer(tc_name)
@@ -535,7 +468,11 @@ def test_ospf_routemaps_functionality_tc20_p0(request):
input_dict = {
"r0": {
"static_routes": [
- {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0",}
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
]
}
}
@@ -543,15 +480,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request):
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": {
- "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)
+ redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
step(
"Verify that routes are not allowed in OSPF even tough no "
@@ -663,318 +592,221 @@ def test_ospf_routemaps_functionality_tc21_p0(request):
step(
"Create static routes(10.0.20.1/32) in R1 and redistribute "
- "to OSPF using route map.")
+ "to OSPF using route map."
+ )
# Create Static routes
input_dict = {
"r0": {
"static_routes": [
{
- "network": NETWORK['ipv4'][0],
+ "network": NETWORK["ipv4"][0],
"no_of_ip": 5,
- "next_hop": 'Null0',
+ "next_hop": "Null0",
}
]
}
}
result = create_static_routes(tgen, input_dict)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(
- tc_name, result)
+ 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)
+ redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
# Create route map
routemaps = {
- "r0": {
- "route_maps": {
- "rmap_ipv4": [{
- "action": "permit",
- "seq_id": 10
- }]
- }
- }
+ "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)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
step("Verify that route is advertised to R2.")
- dut = 'r1'
- protocol = 'ospf'
+ dut = "r1"
+ protocol = "ospf"
result = verify_ospf_rib(tgen, dut, input_dict)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(
- tc_name, result)
+ 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)
+ 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
- }]
+ "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)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- step(' Configure route map with set clause (set metric)')
+ 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
- }]
+ "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)
+ 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'
+ 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)
+ 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).")
+ "some actions(change metric)."
+ )
# Create route map
routemaps = {
- "r0": {
- "route_maps": {
- "rmap_ipv4": [{
- "action": "permit",
- "match": {
- "med": 123
- },
- "set": {
- "med": 150
- },
- "seq_id": 10
- }]
+ "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)
+ 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'
- }]
- }
+ "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)
+ 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
- }]
+ "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)
+ 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)
+ 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)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
# Create route map
- routemaps = {
- "r0": {
- "route_maps": {
- "rmap_ipv4": [{
- "delete": True
- }]
- }
- }
- }
+ 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)
+ 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
+ "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,
},
- "seq_id": 20
- },
- {
- "action": "permit",
- "match": {
- "ipv4": {
- "prefix_lists": "pf_list_1_ipv4"
- }
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "set": {"med": 100},
+ "seq_id": 20,
},
- "set": {
- "med": 50
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "set": {"med": 50},
+ "seq_id": 30,
},
- "seq_id": 30
- }
- ]
+ ]
+ }
}
}
- }
result = create_route_maps(tgen, input_dict_3)
- assert result is True, 'Testcase {} : Failed \n Error: {}'.format(
- tc_name, result)
+ 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)
+ 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)
+ 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"
- }
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "goto": "30",
},
- "goto": "30",
- },
- {
- "action": "permit",
- 'seq_id': '20',
- "match": {
- "ipv4": {
- "prefix_lists": "pf_list_1_ipv4"
- }
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "set": {"med": 100},
},
- "set": {
- "med": 100
- }
- },
- {
- "action": "permit",
- 'seq_id': '30',
- "match": {
- "ipv4": {
- "prefix_lists": "pf_list_1_ipv4"
- }
+ {
+ "action": "permit",
+ "seq_id": "30",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "set": {"med": 200},
},
- "set": {
- "med": 200
- }
- }
- ]
+ ]
+ }
}
}
- }
result = create_route_maps(tgen, input_dict_3)
- assert result is True, 'Testcase {} : Failed \n Error: {}'.format(
- tc_name, result)
+ 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)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
write_test_footer(tc_name)
@@ -1003,22 +835,18 @@ def test_ospf_routemaps_functionality_tc24_p0(request):
input_dict = {
"r0": {
"static_routes": [
- {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "next_hop": "Null0",}
+ {
+ "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)
- 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)
+ redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
# Create ip prefix list
pfx_list = {
@@ -1037,9 +865,10 @@ def test_ospf_routemaps_functionality_tc24_p0(request):
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)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ tc_name, result
)
# Create route map
@@ -1105,9 +934,10 @@ def test_ospf_routemaps_functionality_tc24_p0(request):
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)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ tc_name, result
)
# Create route map
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py
index 6ac0b515df..5aa2779aee 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py
@@ -61,6 +61,7 @@ from lib.ospf import (
clear_ospf,
verify_ospf_rib,
create_router_ospf,
+ redistribute_ospf,
)
# Global variables
@@ -183,42 +184,6 @@ def teardown_module(mod):
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)
-
-
-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.
# ##################################
@@ -407,7 +372,13 @@ def test_ospf_redistribution_tc6_p0(request):
protocol = "ospf"
result = verify_rib(
- tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh, expected=False,
+ tgen,
+ "ipv4",
+ dut,
+ input_dict,
+ protocol=protocol,
+ next_hop=nh,
+ expected=False,
)
assert result is not True, "Testcase {} : Failed \n Error: {}".format(
tc_name, result
@@ -480,8 +451,8 @@ def test_ospf_redistribution_tc8_p1(request):
"advertised/exchaged via ospf"
)
for rtr in topo["routers"]:
- red_static(rtr)
- red_connected(rtr)
+ redistribute_ospf(tgen, topo, rtr, "static")
+ redistribute_ospf(tgen, topo, rtr, "connected")
for node in topo["routers"]:
input_dict = {
"r0": {
@@ -538,18 +509,16 @@ def test_ospf_redistribution_tc8_p1(request):
)
for rtr in topo["routers"]:
- ospf_red = {
- rtr: {"ospf": {"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
- )
+ redistribute_ospf(tgen, topo, rtr, "static", delete=True)
input_dict = {
"r0": {
"static_routes": [
- {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0",}
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
]
}
}
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py
index 3a269d853c..6f6b119abc 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py
@@ -29,6 +29,7 @@ import pytest
import json
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__))
@@ -233,7 +234,7 @@ def test_ospf_p2p_tc3_p0(request):
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(unicode(intf_ip.split("/")[0])) + 3
+ 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)
@@ -284,7 +285,7 @@ def test_ospf_p2p_tc3_p0(request):
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(unicode(intf_ip.split("/")[0])) + 3
+ 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)
@@ -776,7 +777,6 @@ def test_ospf_show_p1(request):
write_test_footer(tc_name)
-
def test_ospf_dead_tc11_p0(request):
"""
OSPF timers.
@@ -798,224 +798,146 @@ def test_ospf_dead_tc11_p0(request):
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
- }
+ "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)
+ 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
- }
- }
- }
- }
- }
+ "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)
+ 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")
+ 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
- }
+ "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)
-
+ 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'
+ 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)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, ("setup_module :Failed \n Error:"
- " {}".format(ospf_covergence))
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
- step(
- "reconfigure the default dead interval timer value to "
- "default on r1 and r2")
+ 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
- }
+ "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)
+ 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
- }
+ "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)
+ 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'
+ 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)
-
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, ("setup_module :Failed \n Error:"
- " {}".format(ospf_covergence))
-
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
step(" Configure dead timer = 65535 on r1 and r2")
topo1 = {
- 'r0': {
- 'links': {
- 'r1': {
- 'interface': topo['routers']['r0']['links']['r1'][
- 'interface'],
- 'ospf': {
- 'dead_interval': 65535
- }
+ "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)
+ 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
- }
+ "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)
+ 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'
+ 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)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, ("setup_module :Failed \n Error:"
- " {}".format(ospf_covergence))
-
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
step(" Try configuring timer values outside range for example 65536")
topo1 = {
- 'r0': {
- 'links': {
- 'r1': {
- 'interface': topo['routers']['r0']['links']['r1'][
- 'interface'],
- 'ospf': {
- 'dead_interval': 65536
- }
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf": {"dead_interval": 65536},
}
}
}
@@ -1023,47 +945,33 @@ def test_ospf_dead_tc11_p0(request):
result = create_interfaces_cfg(tgen, topo1)
assert result is not True, "Testcase {} : Failed \n Error: {}".format(
- tc_name, result)
+ 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
+ "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)
+ 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'
+ "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)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
write_test_footer(tc_name)
diff --git a/tests/topotests/pbr-topo1/test_pbr_topo1.py b/tests/topotests/pbr-topo1/test_pbr_topo1.py
index fcbe3c0adf..5161d5eec7 100644
--- a/tests/topotests/pbr-topo1/test_pbr_topo1.py
+++ b/tests/topotests/pbr-topo1/test_pbr_topo1.py
@@ -80,7 +80,7 @@ class NetworkTopo(Topo):
##
#####################################################
-
+@pytest.mark.pbr
def setup_module(module):
"Setup topology"
tgen = Topogen(NetworkTopo, module.__name__)
diff --git a/tests/topotests/pim-basic/test_pim.py b/tests/topotests/pim-basic/test_pim.py
index e8a9f72b48..224b82f1fb 100644
--- a/tests/topotests/pim-basic/test_pim.py
+++ b/tests/topotests/pim-basic/test_pim.py
@@ -31,6 +31,8 @@ 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, "../"))
diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini
index 6e8e749092..0c45a09445 100644
--- a/tests/topotests/pytest.ini
+++ b/tests/topotests/pytest.ini
@@ -2,6 +2,29 @@
[pytest]
norecursedirs = .git example-test example-topojson-test lib docker
+# Markers
+#
+# Please consult the documentation and discuss with TSC members before applying
+# any changes to this list.
+markers =
+ babeld: Tests that run against BABELD
+ bfdd: Tests that run against BFDD
+ bgpd: Tests that run against BGPD
+ eigrpd: Tests that run against EIGRPD
+ isisd: Tests that run against ISISD
+ ldpd: Tests that run against LDPD
+ nhrpd: Tests that run against NHRPD
+ ospf6d: Tests that run against OSPF6D
+ ospfd: Tests that run against OSPFD
+ pathd: Tests that run against PATHD
+ pbrd: Tests that run against PBRD
+ pimd: Tests that run against PIMD
+ ripd: Tests that run against RIPD
+ ripngd: Tests that run against RIPNGD
+ sharpd: Tests that run against SHARPD
+ staticd: Tests that run against STATICD
+ vrrpd: Tests that run against VRRPD
+
[topogen]
# Default configuration values
#
diff --git a/tests/topotests/rip-topo1/test_rip_topo1.py b/tests/topotests/rip-topo1/test_rip_topo1.py
index de11b78824..edad1ff65d 100644
--- a/tests/topotests/rip-topo1/test_rip_topo1.py
+++ b/tests/topotests/rip-topo1/test_rip_topo1.py
@@ -104,7 +104,7 @@ class NetworkTopo(Topo):
##
#####################################################
-
+@pytest.mark.rip
def setup_module(module):
global topo, net
@@ -352,9 +352,11 @@ def test_zebra_ipv4_routingTable():
else:
print("r%s ok" % i)
- assert failures == 0, (
- "Zebra IPv4 Routing Table verification failed for router r%s:\n%s"
- % (i, diff)
+ assert (
+ failures == 0
+ ), "Zebra IPv4 Routing Table verification failed for router r%s:\n%s" % (
+ i,
+ diff,
)
# Make sure that all daemons are still running
diff --git a/tests/topotests/ripng-topo1/test_ripng_topo1.py b/tests/topotests/ripng-topo1/test_ripng_topo1.py
index 2976cdefe4..47b63e5b26 100644
--- a/tests/topotests/ripng-topo1/test_ripng_topo1.py
+++ b/tests/topotests/ripng-topo1/test_ripng_topo1.py
@@ -104,7 +104,7 @@ class NetworkTopo(Topo):
##
#####################################################
-
+@pytest.mark.rip
def setup_module(module):
global topo, net
@@ -373,9 +373,11 @@ def test_zebra_ipv6_routingTable():
else:
print("r%s ok" % i)
- assert failures == 0, (
- "Zebra IPv6 Routing Table verification failed for router r%s:\n%s"
- % (i, diff)
+ assert (
+ failures == 0
+ ), "Zebra IPv6 Routing Table verification failed for router r%s:\n%s" % (
+ i,
+ diff,
)
# Make sure that all daemons are running
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 0000000000..00d1e17670
--- /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 0000000000..b5ca993da3
--- /dev/null
+++ b/tests/topotests/simple-snmp-test/r1/isisd.conf
@@ -0,0 +1,46 @@
+log stdout debugging
+!
+debug isis route-events
+debug isis events
+!
+interface r1-eth0
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r1-eth1
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r1-eth2
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface lo
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ isis passive
+ no isis hello padding
+!
+router isis ISIS1
+ net 01.1111.0000.0000.0001.00
+ is-type level-1
+ topology ipv6-unicast
+!
+line vty
+!
diff --git a/tests/topotests/simple-snmp-test/r1/snmpd.conf b/tests/topotests/simple-snmp-test/r1/snmpd.conf
new file mode 100644
index 0000000000..b37911da36
--- /dev/null
+++ b/tests/topotests/simple-snmp-test/r1/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:1.1.1.1:161
+
+com2sec public 1.1.1.1 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
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 0000000000..5281d0055d
--- /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 0000000000..88ff01bf0a
--- /dev/null
+++ b/tests/topotests/simple-snmp-test/test_simple_snmp.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+
+#
+# test_simple_snmp.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_simple snmp.py: Test snmp infrastructure.
+"""
+
+import os
+import sys
+import 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.snmptest import SnmpTester
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ # This function only purpose is to define allocation and relationship
+ # between routers, switches and hosts.
+ #
+ #
+ # Create routers
+ tgen.add_router("r1")
+
+ # r1-eth0
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+ # r1-eth1
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r1"])
+
+ # r1-eth2
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r1"])
+
+
+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(TemplateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ r1 = tgen.gears["r1"]
+
+ 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_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.net.get("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/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 0000000000..7099043adc
--- /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 0000000000..91820b0491
--- /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 0000000000..5246a311ea
--- /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 0000000000..bb72578ff3
--- /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 0000000000..a33257de65
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py
@@ -0,0 +1,1261 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+"""
+
+ -Verify static route ECMP functionality with 2 next hop.
+
+ -Verify static route functionality with 2 next hop and different AD
+ value.
+
+ -Verify RIB status when same route advertise via BGP and static route.
+
+"""
+import sys
+import json
+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 mininet.topo import Topo
+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,
+ create_interfaces_cfg,
+ 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_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/static_routes_topo1_ebgp.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {"ipv4": ["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"}
+
+
+class CreateTopo(Topo):
+ """
+ Test CreateTopo - topology 1.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment.
+
+ * `mod`: module name
+ """
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), '4.19') < 0:
+ error_msg = ('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
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_static_route_2nh_p0_tc_1_ebgp(request):
+ """
+ Verify static route ECMP functionality with 2 next hop.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+
+ step(
+ "Configure IPv4 static route (10.1.1.1) in R2 with next hop N1"
+ "(28.1.1.2 ) and N2 (29.1.1.2) , Static route next-hop present on"
+ "R1"
+ )
+ step("ex :- ip route 10.1.1.1/24 28.1.1.2 & ip route 10.1.1.1/24 29.1.1.1")
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ },
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ },
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, static route installed in RIB using show ip route"
+ " with 2 ECMP next hop "
+ )
+ nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+ step("Configure redistribute static in BGP on R2 router")
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove the static route configured with nexthop N1 from running config")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N1 , "
+ "route become active with nexthop N2 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes is still present in RIB".format(
+ tc_name
+ )
+
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N1")
+
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove the static route configured with nexthop N2 from" "running config")
+
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N2 , "
+ "route become active with nexthop N1 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N2")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Shut nexthop interface N1")
+ intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("Only one the nexthops should be active in RIB.")
+
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ protocol=protocol,
+ next_hop=nh,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ dut = "r2"
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \nError: Route is"
+ " missing in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ dut = "r2"
+ step("No shut the nexthop interface N1")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]]
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("Shut nexthop interface N2")
+ intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"]
+ dut = "r2"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ " after shut of nexthop N1 , route become active with "
+ "nexthop N2 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \nError: Route is"
+ " missing in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ step("No shut nexthop interface N2")
+ dut = "r2"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]]
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \nError: Route is"
+ " missing in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ dut = "r2"
+ step(
+ "After reload of FRR router , static route installed"
+ " in RIB and FIB properly ."
+ )
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request):
+ """
+ Verify static route functionality with 2 next hop & different AD value.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+ step(
+ "Configure IPv4 static route (10.1.1.1) in R2 with next hop N1"
+ "(28.1.1.2 ) AD 10 and N2 (29.1.1.2) AD 20 , Static route next-hop"
+ "present on R1 \n ex :- ip route 10.1.1.1/24 28.1.1.2 10 & "
+ "ip route 10.1.1.1/24 29.1.1.2 20"
+ )
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ },
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ },
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 2 next hop , lowest AD nexthop is active "
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+ step("Explicit route is added in R3 for R2 nexthop rechability")
+ rt3_rtes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NEXT_HOP_IP["nh1"][addr_type] + "/32",
+ "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type],
+ },
+ {
+ "network": NEXT_HOP_IP["nh2"][addr_type] + "/32",
+ "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type],
+ },
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rt3_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("Configure redistribute static in BGP on R2 router")
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove the static route configured with nexthop N1 from" "running config")
+ rt1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rt1_nh1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N1 , "
+ "route become active with nexthop N2 and vice versa."
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte1_nh1,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N1")
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rte1_nh1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove the static route configured with nexthop N2 from" "running config")
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rte2_nh2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N2 , "
+ "route become active with nexthop N1 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N2")
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rte2_nh2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Shut nexthop interface N1")
+ intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("after shut of nexthop N1 , route become active with nexthop N2")
+
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte1_nh1,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("No shut the nexthop interface N1")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("Shut nexthop interface N2")
+ intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"]
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ " after shut of nexthop N1 , route become active with "
+ "nexthop N2 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("No shut nexthop interface N2")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ dut = "r3"
+ protocol = "bgp"
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ dut = "r2"
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ step(
+ "After reload of FRR router , static route installed"
+ " in RIB and FIB properly ."
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ "missing in RIB".format(tc_name)
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, rte2_nh2, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+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 0000000000..93320df327
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py
@@ -0,0 +1,1711 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+
+"""
+ -Verify static route functionality with 8 next hop different AD value
+ and BGP ECMP
+
+ -Verify 8 static route functionality with 8 next hop different AD
+
+ -Verify static route with 8 next hop with different AD value and 8
+ EBGP neighbors
+
+ -Verify static route with 8 next hop with different AD value and 8
+ IBGP neighbors
+
+ -Delete the static route and verify the RIB and FIB state
+
+ -Verify 8 static route functionality with 8 ECMP next hop
+"""
+import sys
+import json
+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 mininet.topo import Topo
+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_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/static_routes_topo2_ebgp.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {
+ "ipv4": [
+ "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 |
+ | | | | | |
+ | +--------------+ +--------------+ |
+ +------+ +------+ +------+
+
+"""
+
+
+class CreateTopo(Topo):
+ """
+ Test CreateTopo - topology 1.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+
+ 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...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), '4.19') < 0:
+ error_msg = ('These tests will not run. (have kernel "{}", '
+ 'requires kernel >= 4.19)'.format(platform.release()))
+ pytest.skip(error_msg)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+def populate_nh():
+ NEXT_HOP_IP = {
+ "nh1": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0],
+ },
+ "nh2": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ },
+ "nh3": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link2"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link2"]["ipv6"].split("/")[0],
+ },
+ "nh4": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link3"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link3"]["ipv6"].split("/")[0],
+ },
+ "nh5": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link4"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link4"]["ipv6"].split("/")[0],
+ },
+ "nh6": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link5"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link5"]["ipv6"].split("/")[0],
+ },
+ "nh7": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link6"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link6"]["ipv6"].split("/")[0],
+ },
+ "nh8": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link7"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link7"]["ipv6"].split("/")[0],
+ },
+ }
+ return NEXT_HOP_IP
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_static_rte_with_8ecmp_nh_p1_tc9_ebgp(request):
+ """
+ Verify 8 static route functionality with 8 ECMP next hop
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ NEXT_HOP_IP = populate_nh()
+ step("Configure 8 interfaces / links between R1 and R2")
+ step("Configure 8 interfaces / links between R2 and R3")
+ step("Configure 8 IBGP IPv4 peering between R2 and R3 router.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure 8 IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) , N2(22.1.1.2) , N3(23.1.1.2) , N4(24.1.1.2) ,"
+ "N5(25.1.1.2) , N6(26.1.1.2) , N7(27.1.1.2) , N8(28.1.1.2) ,"
+ "Static route next-hop present on R1"
+ )
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+ nh_all[addr_type] = [
+ NEXT_HOP_IP["nh1"][addr_type],
+ NEXT_HOP_IP["nh2"][addr_type],
+ NEXT_HOP_IP["nh3"][addr_type],
+ NEXT_HOP_IP["nh4"][addr_type],
+ NEXT_HOP_IP["nh5"][addr_type],
+ NEXT_HOP_IP["nh6"][addr_type],
+ NEXT_HOP_IP["nh7"][addr_type],
+ NEXT_HOP_IP["nh8"][addr_type],
+ ]
+
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ step(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+ dut = "r2"
+ protocol = "static"
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "verify that entry is removed from RIB and FIB of R3 "
+ )
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "verify that entry is removed from RIB and FIB of R3 "
+ )
+ nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed\nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N1 to N8, one by one")
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed\nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ protocol = "static"
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ # Shutdown interface
+ dut = "r2"
+ step(
+ " interface which is about to be shut no shut between r1 and r2 is " "%s",
+ topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"],
+ )
+ intf = topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("Random no shut of the nexthop interfaces")
+ # Bringup interface
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "After random shut/no shut of nexthop , only that "
+ "nexthop deleted/added from all the routes , other nexthop remain "
+ "unchanged"
+ )
+ dut = "r2"
+ protocol = "static"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ step("Remove random static route with all the nexthop")
+ dut = "r2"
+ randnum = random.randint(1, 7)
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum)][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After delete of random route , that route only got deleted from"
+ " RIB/FIB other route are showing properly"
+ )
+ nh = NEXT_HOP_IP["nh{}".format(randnum)][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+ start_router(tgen, "r2")
+
+ step(
+ "After reload of FRR router , static route "
+ "installed in RIB and FIB properly ."
+ )
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ nhp = 1
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Verifying %s routes on r2", addr_type)
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ step("Remove the redistribute static knob")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static", "delete": True}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the BGP neighbor or redistribute static knob , "
+ "verify route got clear from RIB and FIB of R3 routes "
+ )
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes are"
+ " still present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ebgp(request):
+ """
+ Verify static route functionality with 8 next hop different AD
+ value and BGP ECMP
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure 8 interfaces / links between R1 and R2 ,")
+ step("Configure 8 interlaces/links between R2 and R3")
+ step(
+ "Configure IBGP IPv4 peering over loopback interface between"
+ "R2 and R3 router."
+ )
+ step("Configure redistribute static in BGP on R2 router")
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ nh_all[addr_type] = []
+ for nhp in range(1, 9):
+ nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30,"
+ "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60,"
+ "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop"
+ "present on R1"
+ )
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 8 next hop , lowest AD nexthop is active"
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ wait=2,
+ attempts=3,
+ )
+ 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,
+ wait=2,
+ attempts=3,
+ )
+ 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,
+ wait=2,
+ attempts=3,
+ )
+ 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 0000000000..255bb073b4
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py
@@ -0,0 +1,1333 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+"""
+ -Verify static route ECMP functionality with 8 next hop
+
+ -Verify static route functionality with 8 next hop different AD value
+
+ -Verify static route with tag option
+
+ -Verify BGP did not install the static route when it receive route
+ with local next hop
+
+"""
+import sys
+import json
+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 mininet.topo import Topo
+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,
+ create_interfaces_cfg,
+ 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_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/static_routes_topo3_ebgp.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {
+ "ipv4": [
+ "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 = []
+
+
+class CreateTopo(Topo):
+ """
+ Test CreateTopo - topology 1.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+
+ 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...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), "4.19") < 0:
+ error_msg = (
+ '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)
+ NEXT_HOP_IP = populate_nh()
+
+ step("Configure 8 interfaces / links between R1 and R2,")
+ step("Configure IBGP IPv4 peering between R2 and R3 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"
+ )
+ step("On R2, static route with lowest AD nexthop installed in FIB")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes "
+ " with high AD are active in RIB".format(tc_name)
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Configuring redistribute static")
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After configuring them, route is always active with lowest AD"
+ "value and all the nexthop populated in RIB and FIB again "
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+
+ step(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one, "
+ "route become active with next preferred nexthop and nexthop which "
+ "got removed is not shown in RIB and FIB"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes are"
+ " still present in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N1 to N8, one by" "one")
+ for addr_type in ADDR_TYPES:
+ # add static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("On R2, static route with lowest AD nexthop installed in FIB")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes "
+ " with high AD are active in RIB".format(tc_name)
+
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ input_dict_5 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_5,
+ next_hop=nhip,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n"
+ "Error: Routes are still present in RIB".format(tc_name)
+
+ step("Random no shut of the nexthop interfaces")
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n"
+ "Error: Routes are missing in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+ start_router(tgen, "r2")
+
+ step(
+ "After reload of FRR router, static route installed "
+ "in RIB and FIB properly ."
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes "
+ " with high AD are active in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_local_nexthop_p1_tc14_ebgp(request):
+ """
+ Verify BGP did not install the static route when it receive route
+ with local next hop
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ step("Configure BGP IPv4 session between R2 and R3")
+ step("Configure IPv4 static route on R2")
+ reset_config_on_routers(tgen)
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r3"]["links"]["r2-link0"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure redistribute static in the BGP")
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify R2 BGP table has IPv4 route")
+ dut = "r2"
+ result = verify_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB of R2".format(tc_name)
+
+ step(" Verify route did not install in the R3 BGP table, RIB/FIB")
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4, expected=False)
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in BGP RIB of R2".format(tc_name)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, expected=False)
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB of R2".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_frr_intf_name_as_gw_gap_tc4_ebgp_p0(request):
+ """
+ Verify static route configure with interface name as gateway'
+ 'address'
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"]
+ nh = topo["routers"]["r1"]["links"]["r2-link0"]
+ ip_list = {
+ "ipv4": [(dut, intf, ["1.1.1.1/32"], nh["ipv4"].split("/")[0])],
+ "ipv6": [(dut, intf, ["4001::32/128"], nh["ipv6"].split("/")[0])],
+ }
+
+ step(
+ "Configure IPv4 and IPv6 static route in FRR with different next"
+ "hop (ens224 as nexthop))"
+ )
+ step("ip route 2.2.2.0/24 20.1.1.1 ens224 ----from FRR cli")
+ step("ipv6 route 2000::1/120 5000::1 ens224 ----from FRR cli")
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ nh = topo["routers"]["r2"]["links"]["r1-link0"][addr_type].split("/")[0]
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {"network": ip_list[addr_type][0][2][0], "next_hop": nh}
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "IPv4 and IPv6 Static route added in FRR verify using "
+ "show ip route , nexthop is resolved using show nht"
+ )
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, next_hop=nh
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ input_dict_nh = {
+ "r1": {
+ nh: {
+ "Address": nh,
+ "resolvedVia": "connected",
+ "nexthops": {"nexthop1": {"Interfcae": intf}},
+ }
+ }
+ }
+ result = verify_ip_nht(tgen, input_dict_nh)
+ assert result is True, "Testcase {} : Failed \nError: Nexthop is"
+ " missing in RIB".format(tc_name)
+
+ step(
+ "Shut / no shut IPv4 and IPv6 static next hop interface from"
+ "kernel and FRR CLI"
+ )
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ "After shut of nexthop interface, IPv4 and IPv6 route got removed "
+ "from RIB verify using show ip route show nht"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ protocol=protocol,
+ next_hop=nh,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "After no shut route got added again in RIB /FIB using "
+ "show ip route nexthop is resolved using show nht"
+ )
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert result is True, "Testcase {} : Failed".format(tc_name)
+
+ for addr_type in ADDR_TYPES:
+ nh = topo["routers"]["r2"]["links"]["r1-link0"][addr_type].split("/")[0]
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": ip_list[addr_type][0][2][0],
+ "next_hop": nh,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Removing FRR configured static route verify FRR route also "
+ "removed from FRR"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ protocol=protocol,
+ next_hop=nh,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes"
+ " still present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_with_tag_p0_tc_13_ebgp(request):
+ """
+ Verify static route with tag option
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure 8 links between R1 and R2")
+ step("Configure 1 links between R2 and R3")
+ NEXT_HOP_IP = populate_nh()
+
+ step(
+ "Configure 2 IPv4 static route (S1 and S2) in R2 with same"
+ "next hop N1 28.1.1.2"
+ )
+ step("Configure static route S1 with tag 1 and static route S2 with" "tag2")
+ step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 1")
+ step("S2= ip route 20.1.1.1/24 28.1.1.2 tag 2")
+ step("Enable redistribute static in BGP with route-map")
+ reset_config_on_routers(tgen)
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "tag": 4001,
+ },
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "tag": 4002,
+ },
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("verify routes are present in RIB")
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ step("Configure route-map on R2 with allow tag1 and deny tag2")
+
+ # Create route map
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "match": {addr_type: {"tag": "4001"}},
+ },
+ {
+ "action": "deny",
+ "seq_id": 20,
+ "match": {addr_type: {"tag": "4002"}},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "route_maps": [
+ {
+ "name": "rmap_match_tag_1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ },
+ "redistribute": [{"redist_type": "static"}],
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify static route S1 advetised in BGP table when tag1 permit"
+ "in route-map else it is denied"
+ )
+ dut = "r3"
+ input_dict_0 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "tag": 4002,
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_0, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route with "
+ "tag 4002 is still present in RIB".format(tc_name)
+
+ dut = "r2"
+ input_dict_1 = {
+ "r2": {"static_routes": [{"network": NETWORK[addr_type], "tag": 4001}]}
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_0, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \nError: Route with "
+ "tag 4001 is missing in RIB".format(tc_name)
+
+ step("Modify the route-map to allow tag2 and deny tag1")
+ # Create route map
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format(addr_type): [
+ {
+ "action": "deny",
+ "seq_id": 10,
+ "match": {addr_type: {"tag": "4001"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": 20,
+ "match": {addr_type: {"tag": "4002"}},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r3"
+ step(
+ "Verify static route S2 advertised in BGP table when tag2"
+ "permit in route-map else it is denied"
+ )
+ protocol = "bgp"
+ input_dict_0 = {
+ "r2": {"static_routes": [{"network": NETWORK2[addr_type], "tag": 4002}]}
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_0, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \nError: Route with "
+ "tag 4002 is missing in RIB".format(tc_name)
+
+ input_dict_1 = {
+ "r2": {"static_routes": [{"network": NETWORK[addr_type], "tag": 4001}]}
+ }
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_1, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route with "
+ "tag 4001 is still present in RIB".format(tc_name, result)
+
+ 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 0000000000..75657a8895
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py
@@ -0,0 +1,994 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+
+Following tests are covered in the script.
+
+- Verify static route are blocked from route-map and prefix-list
+ applied in BGP nbrs
+- Verify Static route when FRR connected to 2 TOR
+"""
+
+import sys
+import json
+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 mininet.topo import Topo
+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,
+ create_interfaces_cfg,
+ 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_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/static_routes_topo4_ebgp.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {"ipv4": "2.2.2.2/32", "ipv6": "22:22::2/128"}
+NEXT_HOP_IP = {}
+
+
+class CreateTopo(Topo):
+ """
+ Test CreateTopo - topology 1.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ 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...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), '4.19') < 0:
+ error_msg = ('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 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 modfiy 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, 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" "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 0000000000..99b197366a
--- /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 0000000000..47596a0a1a
--- /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 0000000000..4e27229f34
--- /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 0000000000..bb72578ff3
--- /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 0000000000..130f4fd9aa
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py
@@ -0,0 +1,1083 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+"""
+
+ -Verify static route ECMP functionality with 2 next hop
+
+ -Verify static route functionality with 2 next hop and different AD
+ value
+
+ -Verify RIB status when same route advertise via BGP and static route
+
+"""
+import sys
+import json
+import time
+import os
+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/"))
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+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,
+ create_interfaces_cfg,
+ 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_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/static_routes_topo1_ibgp.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {"ipv4": ["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"}
+
+
+class CreateTopo(Topo):
+ """
+ Test CreateTopo - topology 1.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+
+ 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...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), '4.19') < 0:
+ error_msg = ('These tests will not run. (have kernel "{}", '
+ 'requires kernel >= 4.19)'.format(platform.release()))
+ pytest.skip(error_msg)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+def populate_nh():
+ NEXT_HOP_IP = {
+ "nh1": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0],
+ },
+ "nh2": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ },
+ }
+ return NEXT_HOP_IP
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def test_static_route_2nh_p0_tc_1_ibgp(request):
+ """
+ Verify static route ECMP functionality with 2 next hop
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+
+ step(
+ "Configure IPv4 static route (10.1.1.1) in R2 with next hop N1"
+ "(28.1.1.2 ) and N2 (29.1.1.2) , Static route next-hop present on"
+ "R1"
+ )
+ step("ex :- ip route 10.1.1.1/24 28.1.1.2 & ip route 10.1.1.1/24 29.1.1.1")
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ },
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ },
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, static route installed in RIB using show ip route"
+ " with 2 ECMP next hop "
+ )
+ nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+ step("Configure redistribute static in BGP on R2 router")
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove the static route configured with nexthop N1 from running config")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N1 , "
+ "route become active with nexthop N2 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N1")
+
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove the static route configured with nexthop N2 from running config")
+
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N2 , "
+ "route become active with nexthop N1 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N2")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Shut nexthop interface N1")
+ intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("Only one the nexthops should be active in RIB.")
+
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ protocol=protocol,
+ next_hop=nh,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ dut = "r2"
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \nError: Route is"
+ " missing in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ dut = "r2"
+ step("No shut the nexthop interface N1")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]]
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("Shut nexthop interface N2")
+ intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"]
+ dut = "r2"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ " after shut of nexthop N1 , route become active with "
+ "nexthop N2 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \nError: Route is"
+ " missing in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ step("No shut nexthop interface N2")
+ dut = "r2"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]]
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \nError: Route is"
+ " missing in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ dut = "r2"
+ step(
+ "After reload of FRR router , static route installed"
+ " in RIB and FIB properly ."
+ )
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route is"
+ " still present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request):
+ """
+ Verify static route functionality with 2 next hop & different AD value
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+ step(
+ "Configure IPv4 static route (10.1.1.1) in R2 with next hop N1"
+ "(28.1.1.2 ) AD 10 and N2 (29.1.1.2) AD 20 , Static route next-hop"
+ "present on R1 \n ex :- ip route 10.1.1.1/24 28.1.1.2 10 & "
+ "ip route 10.1.1.1/24 29.1.1.2 20"
+ )
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ },
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ },
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 2 next hop , lowest AD nexthop is active "
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+ step("Explicit route is added in R3 for R2 nexthop rechability")
+ rt3_rtes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NEXT_HOP_IP["nh1"][addr_type] + "/32",
+ "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type],
+ },
+ {
+ "network": NEXT_HOP_IP["nh2"][addr_type] + "/32",
+ "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type],
+ },
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rt3_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("Configure redistribute static in BGP on R2 router")
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove the static route configured with nexthop N1 from running config")
+ rt1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rt1_nh1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N1 , "
+ "route become active with nexthop N2 and vice versa."
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte1_nh1,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N1")
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rte1_nh1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove the static route configured with nexthop N2 from running config")
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rte2_nh2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N2 , "
+ "route become active with nexthop N1 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N2")
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rte2_nh2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Shut nexthop interface N1")
+ intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("after shut of nexthop N1 , route become active with nexthop N2")
+
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte1_nh1,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("No shut the nexthop interface N1")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("Shut nexthop interface N2")
+ intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"]
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ " after shut of nexthop N1 , route become active with "
+ "nexthop N2 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ step("No shut nexthop interface N2")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ dut = "r3"
+ protocol = "bgp"
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ dut = "r2"
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ step(
+ "After reload of FRR router , static route installed"
+ " in RIB and FIB properly ."
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ "missing in RIB".format(tc_name)
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, rte2_nh2, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ "not active in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py
new file mode 100644
index 0000000000..0a757c9f28
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py
@@ -0,0 +1,1974 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+
+"""
+ -Verify static route functionality with 8 next hop different AD value
+ and BGP ECMP
+
+ -Verify 8 static route functionality with 8 next hop different AD
+
+ -Verify static route with 8 next hop with different AD value and 8
+ EBGP neighbors
+
+ -Verify static route with 8 next hop with different AD value and 8
+ IBGP neighbors
+
+ -Delete the static route and verify the RIB and FIB state
+
+ -Verify 8 static route functionality with 8 ECMP next hop
+"""
+import sys
+import json
+import time
+import os
+import pytest
+import platform
+from time import sleep
+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 mininet.topo import Topo
+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_interfaces_cfg,
+ 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_topo_from_json, build_config_from_json
+from lib.topotest import version_cmp
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/static_routes_topo2_ibgp.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {
+ "ipv4": [
+ "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 |
+ | | | | | |
+ | +--------------+ +--------------+ |
+ +------+ +------+ +------+
+
+"""
+
+
+class CreateTopo(Topo):
+ """
+ Test CreateTopo - topology 1.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+
+ 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...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), '4.19') < 0:
+ error_msg = ('These tests will not run. (have kernel "{}", '
+ 'requires kernel >= 4.19)'.format(platform.release()))
+ pytest.skip(error_msg)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+def populate_nh():
+ NEXT_HOP_IP = {
+ "nh1": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0],
+ },
+ "nh2": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ },
+ "nh3": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link2"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link2"]["ipv6"].split("/")[0],
+ },
+ "nh4": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link3"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link3"]["ipv6"].split("/")[0],
+ },
+ "nh5": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link4"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link4"]["ipv6"].split("/")[0],
+ },
+ "nh6": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link5"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link5"]["ipv6"].split("/")[0],
+ },
+ "nh7": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link6"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link6"]["ipv6"].split("/")[0],
+ },
+ "nh8": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link7"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link7"]["ipv6"].split("/")[0],
+ },
+ }
+ return NEXT_HOP_IP
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def test_static_rte_with_8ecmp_nh_p1_tc9_ibgp(request):
+ """
+ Verify 8 static route functionality with 8 ECMP next hop
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ NEXT_HOP_IP = populate_nh()
+ step("Configure 8 interfaces / links between R1 and R2")
+ step("Configure 8 interfaces / links between R2 and R3")
+ step("Configure 8 IBGP IPv4 peering between R2 and R3 router.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure 8 IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) , N2(22.1.1.2) , N3(23.1.1.2) , N4(24.1.1.2) ,"
+ "N5(25.1.1.2) , N6(26.1.1.2) , N7(27.1.1.2) , N8(28.1.1.2) ,"
+ "Static route next-hop present on R1"
+ )
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+ nh_all[addr_type] = [
+ NEXT_HOP_IP["nh1"][addr_type],
+ NEXT_HOP_IP["nh2"][addr_type],
+ NEXT_HOP_IP["nh3"][addr_type],
+ NEXT_HOP_IP["nh4"][addr_type],
+ NEXT_HOP_IP["nh5"][addr_type],
+ NEXT_HOP_IP["nh6"][addr_type],
+ NEXT_HOP_IP["nh7"][addr_type],
+ NEXT_HOP_IP["nh8"][addr_type],
+ ]
+
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ step(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+ dut = "r2"
+ protocol = "static"
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "verify that entry is removed from RIB and FIB of R3 "
+ )
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "verify that entry is removed from RIB and FIB of R3 "
+ )
+ nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed\nError: Routes is"
+ " still present in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N1 to N8, one by one")
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed\nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ protocol = "static"
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ # Shutdown interface
+ dut = "r2"
+ step(
+ " interface which is about to be shut no shut between r1 and r2 is " "%s",
+ topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"],
+ )
+ intf = topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("Random no shut of the nexthop interfaces")
+ # Bringup interface
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "After random shut/no shut of nexthop , only that "
+ "nexthop deleted/added from all the routes , other nexthop remain "
+ "unchanged"
+ )
+ dut = "r2"
+ protocol = "static"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ step("Remove random static route with all the nexthop")
+ dut = "r2"
+ randnum = random.randint(1, 7)
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum)][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After delete of random route , that route only got deleted from"
+ " RIB/FIB other route are showing properly"
+ )
+ nh = NEXT_HOP_IP["nh{}".format(randnum)][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+ start_router(tgen, "r2")
+
+ step(
+ "After reload of FRR router , static route "
+ "installed in RIB and FIB properly ."
+ )
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ nhp = 1
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Verifying %s routes on r2", addr_type)
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ step("Remove the redistribute static knob")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static", "delete": True}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the BGP neighbor or redistribute static knob , "
+ "verify route got clear from RIB and FIB of R3 routes "
+ )
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes are"
+ " still present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ibgp(request):
+ """
+ Verify static route functionality with 8 next hop different AD
+ value and BGP ECMP
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure 8 interfaces / links between R1 and R2 ,")
+ step("Configure 8 interlaces/links between R2 and R3")
+ step(
+ "Configure IBGP IPv4 peering over loopback interface between"
+ "R2 and R3 router."
+ )
+ step("Configure redistribute static in BGP on R2 router")
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ nh_all[addr_type] = []
+ for nhp in range(1, 9):
+ nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30,"
+ "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60,"
+ "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop"
+ "present on R1"
+ )
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 8 next hop , lowest AD nexthop is active"
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ wait=2,
+ attempts=3,
+ )
+ 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,
+ wait=2,
+ attempts=3,
+ )
+ 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 \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"
+ " 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,
+ wait=2,
+ attempts=3,
+ )
+ 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 0000000000..924fb3a598
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py
@@ -0,0 +1,875 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+"""
+ -Verify static route ECMP functionality with 8 next hop
+
+ -Verify static route functionality with 8 next hop different AD value
+
+ -Verify static route with tag option
+
+ -Verify BGP did not install the static route when it receive route
+ with local next hop
+
+"""
+import sys
+import json
+import time
+import os
+import pytest
+import platform
+from copy import deepcopy
+import random
+from re import search as re_search
+
+
+# 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 mininet.topo import Topo
+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,
+ create_interfaces_cfg,
+ 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_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/static_routes_topo3_ibgp.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {
+ "ipv4": [
+ "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 = []
+
+
+class CreateTopo(Topo):
+ """
+ Test CreateTopo - topology 1.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+
+ 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...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), '4.19') < 0:
+ error_msg = ('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)
+ NEXT_HOP_IP = populate_nh()
+
+ step("Configure 8 interfaces / links between R1 and R2,")
+ step("Configure IBGP IPv4 peering between R2 and R3 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"
+ )
+ 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"
+ " strill present in RIB of R3".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_local_nexthop_p1_tc14_ibgp(request):
+ """
+ Verify BGP did not install the static route when it receive route
+ with local next hop
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ step("Configure BGP IPv4 session between R2 and R3")
+ step("Configure IPv4 static route on R2")
+ reset_config_on_routers(tgen)
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r3"]["links"]["r2-link0"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure redistribute static in the BGP")
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify R2 BGP table has IPv4 route")
+ dut = "r2"
+ result = verify_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB of R2".format(tc_name)
+
+ step(" Verify route did not install in the R3 BGP table, RIB/FIB")
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4, expected=False)
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in BGP RIB of R2".format(tc_name)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, expected=False)
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB of R2".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py
new file mode 100644
index 0000000000..fdbfad25b3
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py
@@ -0,0 +1,991 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+
+Following tests are covered in the script.
+
+- Verify static route are blocked from route-map and prefix-list
+ applied in BGP nbrs
+"""
+
+import sys
+import json
+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 mininet.topo import Topo
+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,
+ create_interfaces_cfg,
+ 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_topo_from_json, build_config_from_json
+from lib.topotest import version_cmp
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/static_routes_topo4_ibgp.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {"ipv4": "2.2.2.2/32", "ipv6": "22:22::2/128"}
+NEXT_HOP_IP = {}
+
+
+class CreateTopo(Topo):
+ """
+ Test CreateTopo - topology 1.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ 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...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), '4.19') < 0:
+ error_msg = ('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 modfiy 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" "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/zebra_netlink/test_zebra_netlink.py b/tests/topotests/zebra_netlink/test_zebra_netlink.py
index 4da5303641..94baf8438f 100644
--- a/tests/topotests/zebra_netlink/test_zebra_netlink.py
+++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py
@@ -118,7 +118,12 @@ def test_zebra_netlink_batching():
r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 100")
json_file = "{}/r1/v4_route.json".format(CWD)
expected = json.loads(open(json_file).read())
- test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expected,)
+ test_func = partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip route json",
+ expected,
+ )
_, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5)
assertmsg = '"r1" JSON output mismatches'
assert result is None, assertmsg
diff --git a/tests/topotests/zebra_rib/r1/iproute.ref b/tests/topotests/zebra_rib/r1/iproute.ref
new file mode 100644
index 0000000000..b28182c2d1
--- /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 0000000000..47a9eb6a49
--- /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 0000000000..2de98bd514
--- /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/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py
index ef4b597206..daf8f7be20 100644
--- a/tests/topotests/zebra_rib/test_zebra_rib.py
+++ b/tests/topotests/zebra_rib/test_zebra_rib.py
@@ -41,6 +41,7 @@ sys.path.append(os.path.join(CWD, "../"))
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
+from time import sleep
# Required to instantiate the topology builder class.
from mininet.topo import Topo
@@ -75,8 +76,9 @@ def setup_module(mod):
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))
- )
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)))
+ router.load_config(
+ TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)))
# Initialize all routers.
tgen.start_router()
@@ -157,6 +159,111 @@ def test_zebra_kernel_override():
_, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5)
assert result is None, '"r1" JSON output mismatches'
+def test_route_map_usage():
+ "Test that FRR only reruns over routes associated with the routemap"
+ logger.info("Test that FRR runs on selected re's on route-map changes")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("Skipped because of previous test failure")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ r1 = tgen.gears["r1"]
+ # set the delay timer to 1 to improve test coverage (HA)
+ r1.vtysh_cmd("conf\nzebra route-map delay-timer 1")
+ r1.vtysh_cmd("conf\nroute-map static permit 10\nset src 192.168.215.1")
+ r1.vtysh_cmd("conf\naccess-list 5 seq 5 permit 10.0.0.44/32")
+ r1.vtysh_cmd("conf\naccess-list 10 seq 5 permit 10.0.1.0/24")
+ r1.vtysh_cmd("conf\nroute-map sharp permit 10\nmatch ip address 10\nset src 192.168.214.1")
+ r1.vtysh_cmd("conf\nroute-map sharp permit 20\nset src 192.168.213.1")
+ r1.vtysh_cmd("conf\nip protocol static route-map static")
+ r1.vtysh_cmd("conf\nip protocol sharp route-map sharp")
+ sleep(4)
+ r1.vtysh_cmd("conf\nip route 10.100.100.100/32 192.168.216.3")
+ r1.vtysh_cmd("conf\nip route 10.100.100.101/32 10.0.0.44")
+ r1.vtysh_cmd("sharp install route 10.0.0.0 nexthop 192.168.216.3 500")
+ sleep(4)
+
+ static_rmapfile = "%s/r1/static_rmap.ref" % (thisDir)
+ expected = open(static_rmapfile).read().rstrip()
+ expected = ('\n'.join(expected.splitlines()) + '\n').rstrip()
+ actual = r1.vtysh_cmd("show route-map static")
+ actual = ('\n'.join(actual.splitlines()) + '\n').rstrip()
+ logger.info("Does the show route-map static command run the correct number of times")
+
+ diff = topotest.get_textdiff(actual, expected,
+ title1 = "Actual Route-map output",
+ title2 = "Expected Route-map output")
+ if diff:
+ logger.info("Actual:")
+ logger.info(actual)
+ logger.info("Expected:")
+ logger.info(expected)
+ srun = r1.vtysh_cmd("show run")
+ srun = ('\n'.join(srun.splitlines()) + '\n').rstrip()
+ logger.info("Show run")
+ logger.info(srun)
+ assert 0, "r1 static route processing:\n"
+
+ sharp_rmapfile = "%s/r1/sharp_rmap.ref" % (thisDir)
+ expected = open(sharp_rmapfile).read().rstrip()
+ expected = ('\n'.join(expected.splitlines()) + '\n').rstrip()
+ actual = r1.vtysh_cmd("show route-map sharp")
+ actual = ('\n'.join(actual.splitlines()) + '\n').rstrip()
+ logger.info("Does the show route-map sharp command run the correct number of times")
+
+ diff = topotest.get_textdiff(actual, expected,
+ title1 = "Actual Route-map output",
+ title2 = "Expected Route-map output")
+ if diff:
+ logger.info("Actual:")
+ logger.info(actual)
+ logger.info("Expected:")
+ logger.info(expected)
+ srun = r1.vtysh_cmd("show run")
+ srun = ('\n'.join(srun.splitlines()) + '\n').rstrip()
+ logger.info("Show run:")
+ logger.info(srun)
+ assert 0, "r1 sharp route-map processing:\n"
+
+ logger.info("Add a extension to the static route-map to see the static route go away")
+ r1.vtysh_cmd("conf\nroute-map sharp deny 5\nmatch ip address 5")
+ sleep(2)
+ # 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
+ logger.info("Test that the routes installed are correct")
+ sharp_ipfile = "%s/r1/iproute.ref" % (thisDir)
+ expected = open(sharp_ipfile).read().rstrip()
+ expected = ('\n'.join(expected.splitlines()) + '\n').rstrip()
+ 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)
+ diff = topotest.get_textdiff(actual, expected,
+ title1 = "Actual ip route show",
+ title2 = "Expected ip route show")
+
+ if diff:
+ logger.info("Actual:")
+ logger.info(actual)
+ logger.info("Expected:")
+ logger.info(expected)
+ srun = r1.vtysh_cmd("show run")
+ srun = ('\n'.join(srun.splitlines()) + '\n').rstrip()
+ logger.info("Show run:")
+ logger.info(srun)
+ assert 0, "r1 ip route show is not correct:"
def test_memory_leak():
"Run the memory leak test and report results."
diff --git a/tests/zebra/test_lm_plugin.c b/tests/zebra/test_lm_plugin.c
new file mode 100644
index 0000000000..4a9344fee4
--- /dev/null
+++ b/tests/zebra/test_lm_plugin.c
@@ -0,0 +1,134 @@
+/*
+ * Label Manager tests.
+ * Copyright (C) 2020 Volta Networks
+ * Patrick Ruddy
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#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()
+{
+ /* 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()
+{
+ /* 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()
+{
+ 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 0000000000..bf4f3cef91
--- /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 0000000000..35824f1055
--- /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